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

Does the global keyword inline the global exactly as a local variable declaration in python?

所以这两个 完全 相对于性能等效(即,生成的代码是否完全等效):

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

不,它们并不完全等同,尽管差异不太可能很大。

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 不会创建全局的本地副本,因为没有简单的方法可以确保代码为 运行 时全局值不会更改。任何东西,甚至是另一个线程或调试器,都可以在循环执行时修改该值。

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

每当您尝试访问变量时,Python 将按以下顺序遍历作用域:

  • 本地。当前函数作用域的本地命名空间。
  • 封闭函数局部变量。根据嵌套的数量 functions/lambda,可能会有更多。
  • 全球。全局范围,它只是另一个字典(您可以通过 globals() 访问它)
  • 内置。标准 Python 内置函数可用于所有范围,例如 listint 等。

访问 function/class 属性的方式类似,但涉及:

  • __getattribute__
  • __dict__
  • __getattr__

而且总体上也继承了 类。

邓肯完美地回答了你剩下的问题