Python代码的执行顺序

4
我想知道Python是否与C有类似的问题,涉及代码某些元素的执行顺序。
例如,我知道在C中有时不能保证某个变量在另一个变量之前初始化。或者仅因为一行代码在另一行代码上方,并不意味着它在所有下方的代码之前被实现。
对于Python也是这样吗?比如,如果我打开一个数据文件,读取数据,关闭文件,然后做其他事情,我能确定文件在关闭之前就已经关闭了吗?
我问这个问题是因为我正在尝试读取一个大文件(1.6GB)并使用针对我的工作的特定Python模块。当我运行此模块时,我会收到此错误消息:
    File "/glast01/software/ScienceTools/ScienceTools-v9r15p2-SL4/sane/v3r18p1/python/GtApp.py", line 57, in run
    input, output = self.runWithOutput(print_command)
  File "/glast01/software/ScienceTools/ScienceTools-v9r15p2-SL4/sane/v3r18p1/python/GtApp.py", line 77, in runWithOutput
    return os.popen4(self.command(print_command))
  File "/Home/eud/jmcohen/.local/lib/python2.5/os.py", line 690, in popen4
    stdout, stdin = popen2.popen4(cmd, bufsize)
  File "/Home/eud/jmcohen/.local/lib/python2.5/popen2.py", line 199, in popen4
    inst = Popen4(cmd, bufsize)
  File "/Home/eud/jmcohen/.local/lib/python2.5/popen2.py", line 125, in __init__
    self.pid = os.fork()
OSError: [Errno 12] Cannot allocate memory
>>> 
Exception exceptions.AttributeError: AttributeError("Popen4 instance has no attribute 'pid'",) in <bound method Popen4.__del__ of <popen2.Popen4 instance at 0x9ee6fac>> ignored

我猜测这与我读取的数据量有关(它有17608310行和22列)。我认为如果我在读取数据后立即关闭打开的文件可能会有所帮助,但实际上并没有。这让我开始思考代码执行的顺序,因此提出了我的问题。
谢谢。

10
我认为你需要更好地解释自己。在C语言中,给定 f(); g();,保证会先计算 f() 再计算 g()。而在 a = f() + g(); 中,C语言并未规定计算的顺序。Python保证从左到右进行计算:http://docs.python.org/reference/expressions.html#evaluation-order - Alok Singhal
2
你说的“issues”是指“规则”,对吗? - KevinDTimm
1
是的,我知道我没有解释清楚,抱歉,这是因为我并没有完全理解所有这些内容,但还是试图去做 :)Alok: 是的,基本上这就是我想的。我认为它也适用于一些文件关闭的情况。我知道我可以定义两个不同的函数,并从第一个函数调用第二个函数(我所说的第二个函数是指代码中较低的部分),因此在这种意义上,它不一定按顺序执行。kevindtimm: 是的,我指的是规则。我猜我只是用“问题”这个词,因为它让我感到困扰 :) - Jamie
1
@Jamie:请不要在自己的问题下写冗长、难以阅读的评论。请修正你的问题并删除评论。你拥有这个问题,可以编辑它直到清晰明了。 - S.Lott
1
为什么关闭文件会影响内存消耗?如果你一次性读取1.6G的数据到你设计的任何内存结构中(这可能是非常不优化的),释放一个小文件句柄并不会改变任何东西。你真的必须将整个文件加载到内存中,还是可以逐行处理文件? - Tim Pietzcker
一个打开的文件占用的内存很少。你应该关注你的内存使用情况,而不必担心Python会重新排列你的代码。 - Winston Ewert
7个回答

12

我唯一能想到有可能让一些人感到惊讶的是:

def test():
    try:
        return True
    finally:
        return False

print test()

输出:

False

finally代码块确实是最后执行的,即使在它之前出现了return语句。然而,这不仅适用于Python。


3

C语言的执行顺序对于实际语句来说确实是顺序的。甚至有规则定义了序列点,因此您可以知道个别表达式的求值方式。


3

CPython本身是以这样的方式编写的,即最小化任何像您提到的那样的影响;除了编译期间的字面评估之外,代码始终从上到下执行,对象在其引用计数达到0时立即进行垃圾回收等。


3

在cpython虚拟机中,执行是非常线性的。我认为您遇到的任何问题与执行顺序无关。

但是有一点需要注意,在Python中而不是C中:异常可以在任何地方抛出,因此仅仅因为你在相应的open()调用下看到了close()调用并不意味着该调用实际上已被执行。请使用try/finally(或者在足够新的Python中使用with语句)来确保打开的文件已关闭(以及其他类型的资源已被显式释放)。

如果您的问题出在内存使用上,而不是其他类型的资源上,那么调试可能会更加困难。在Python中,内存不能被显式释放。cpython虚拟机(您很可能正在使用它)会在最后一个引用消失时立即释放内存,但有时可能无法释放被陷在具有__del__方法的对象的循环中的内存。如果您有自己的__del__方法或使用具有它们的类,则这可能是问题的一部分。

不过,您的实际问题(内存问题,而不是执行顺序问题)很难在没有看到更多代码的情况下回答。也许有些明显的解决办法(或者至少可以找到一些明显的减少所需内存量的方法)。


1
“如果我打开一个数据文件,读取数据,关闭文件,然后做其他事情,我可以确定在关闭文件之后执行的行之前文件已经关闭了吗?”
“是的,文件已经关闭。”
“从内存中释放?不一定。不能保证垃圾回收会在何时发生。”
“此外,关闭文件并不意味着您创建的所有其他变量和附加到这些变量的其他对象都已清除。”
“这不是一个‘操作顺序’问题。”
“我敢打赌,您有太多的全局变量,其中包含太多的数据副本。”

0
如果数据由列和行组成,为什么不使用内置的文件迭代器逐行获取呢?
f = open('file.txt')
first_line = f.next()

0

popen2.py:

class Popen4(Popen3):
    childerr = None

    def __init__(self, cmd, bufsize=-1):
        _cleanup()
        self.cmd = cmd
        p2cread, p2cwrite = os.pipe()
        c2pread, c2pwrite = os.pipe()
        self.pid = os.fork()
        if self.pid == 0:
            # Child
            os.dup2(p2cread, 0)
            os.dup2(c2pwrite, 1)
            os.dup2(c2pwrite, 2)
            self._run_child(cmd)
        os.close(p2cread)
        self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
        os.close(c2pwrite)
        self.fromchild = os.fdopen(c2pread, 'r', bufsize)

man 2 fork

fork()函数可能会失败,如果:

[ENOMEM]
        没有足够的存储空间。

os.popen4最终调用open2.Popen4.__init__,必须fork以创建您尝试读取/写入的子进程。这个底层调用失败了,很可能是由于资源耗尽。

您可能在其他地方使用了太多内存,导致fork尝试使用超过给定用户的RLIMIT_DATA或RLIMIT_RSS限制。正如Python memory profiler - Stack Overflow所建议的那样,Heapy可以帮助您确定是否存在这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接