global关键字是否与python中的局部变量声明完全内联?

时间:2016-03-23 12:33:23

标签: python performance python-2.7 global globals

这两个完全等同于性能(即,生成的代码完全等效):

class A(object):
    const = 'abc'

    def lengthy_op(self):
        const = self.const
        for i in xrange(AVOGADRO):
            # do something which involves reading const

const = 'abc'
class A(object):

    def lengthy_op(self):
        global const
        for i in xrange(AVOGADRO):
            # do something which involves reading const

2 个答案:

答案 0 :(得分:1)

不,他们不是完全等同,虽然差异不大可能。

class A(object):
    const = 'abc'

    def lengthy_op(self):
        const = self.const
        for i in xrange(AVOGADRO):
            # do something which involves reading const

这会创建一个局部变量,因此const的任何访问都将使用LOAD_FAST操作码。

const = 'abc'
class A(object):

    def lengthy_op(self):
        # global const
        for i in xrange(AVOGADRO):
            # do something which involves reading const

有或没有冗余global const使用LOAD_GLOBAL来访问全局变量constxrangeAVOGADRO的值。

在C中,Python LOAD_GLOBAL将执行快速字典查找以访问变量(快速,因为全局变量只使用字符串键在字典中,并且哈希值是预先计算的)。另一方面,LOAD_FAST只是访问第一个,第二个,第三个等局部变量,这是一个数组索引操作。

其他版本的Python(例如PyPy)可能能够优化访问全局变量,在这种情况下可能没有任何差异。

第一个代码(n=i+const作为循环体)反汇编为:

>>> dis.dis(A.lengthy_op)
  5           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (const)
              6 STORE_FAST               1 (const)

  6           9 SETUP_LOOP              30 (to 42)
             12 LOAD_GLOBAL              1 (xrange)
             15 LOAD_GLOBAL              2 (AVOGADRO)
             18 CALL_FUNCTION            1
             21 GET_ITER            
        >>   22 FOR_ITER                16 (to 41)
             25 STORE_FAST               2 (i)

  8          28 LOAD_FAST                2 (i)
             31 LOAD_FAST                1 (const)
             34 BINARY_ADD          
             35 STORE_FAST               3 (n)
             38 JUMP_ABSOLUTE           22
        >>   41 POP_BLOCK           
        >>   42 LOAD_CONST               0 (None)
             45 RETURN_VALUE        

而第二个块给出:

>>> dis.dis(A.lengthy_op)
  5           0 SETUP_LOOP              30 (to 33)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_GLOBAL              1 (AVOGADRO)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                16 (to 32)
             16 STORE_FAST               1 (i)

  7          19 LOAD_FAST                1 (i)
             22 LOAD_GLOBAL              2 (const)
             25 BINARY_ADD          
             26 STORE_FAST               2 (n)
             29 JUMP_ABSOLUTE           13
        >>   32 POP_BLOCK           
        >>   33 LOAD_CONST               0 (None)
             36 RETURN_VALUE        

Python不会制作全局的本地副本,因为没有简单的方法可以确保在代码运行时全局值不会发生变化。任何东西,甚至是另一个线程或调试器,都可以在循环执行时修改该值。

答案 1 :(得分:0)

实际上是更快还是更慢取决于您的范围,范围存储在字典中,字典越小(访问速度越快)。由于字典被实现为散列集,因此查找性能为O(1)。

每当您尝试访问变量时,Python都会按以下顺序遍历范围:

  • 本地。本地命名空间,它是当前的函数范围。
  • 封闭功能本地人。根据嵌套函数/ lambda的数量,可以有更多这些。
  • 全局。全局范围只是另一个字典(您可以通过globals()访问)
  • 内置插件。所有范围内都有的标准Python内置函数,例如listint等。

访问函数/类属性的工作方式与此类似,但涉及:

  • __getattribute__
  • __dict__
  • __getattr__

这也是所有继承类的。

Duncan完全回答了你的其余问题