global关键字是否完全像python中的局部变量声明一样内联global?
那么这两种性能是否完全相同(即生成的代码是否完全相同): 以及: 不,它们并不完全相同,尽管差别不大可能很大global关键字是否完全像python中的局部变量声明一样内联global?,python,performance,python-2.7,global,globals,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 这将创建一个局部变量,因此对
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\u FAST
opcode
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\u global
来访问全局变量const
、xrange
和AVOGADRO
的值
在C Python中,LOAD_GLOBAL将执行快速字典查找以访问变量(快速,因为全局变量在字典中仅使用字符串键,并且哈希值是预先计算的)<另一方面,code>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都将按以下顺序遍历作用域:
- 本地的。作为当前函数作用域的本地命名空间
- 封闭函数局部变量。根据嵌套函数/lambda的数量,可以有更多的嵌套函数
- 全球。全局范围,它只是另一个字典(您可以通过
访问)globals()
- 内置的。在所有范围内可用的标准Python内置程序,如
、list
等int
\uuuu获取属性\uuuuu
\uuuu指令
\uuuu getattr\uuuuu
Duncan完美地回答了您的其余问题如果您以只读方式使用变量,则不需要全局关键字。我希望点访问会稍微慢一点,但像这样的问题的答案总是“测量它”。无论如何,这不太可能成为性能瓶颈。@Rogalski:我知道不需要它——我的问题是它是否在局部范围内联全局(使其与另一个等效)。在两种情况下仅使用名称“const”的循环中,点访问执行一次,全局查找也执行一次(我的问题是它们之后是否等效)。这将由能够产生字节码的人立即回答,而不是通过测量,它应该是确定的-除非我遗漏了什么…我希望全局访问会更慢,因为变量名首先在局部范围的符号表中搜索,在全局中,只有在找不到任何内容时,
global
说明符才是真正的说明符,虽然对正确性来说是不必要的,但可能会提高性能,因为它会绕过本地范围内的任何检查。@TomKarzes:正是我的观点-现在如果我们可以删除“may”…ha-那么添加global关键字并不等同于global const;局部常数=常数然后使用本地常量
。有什么原因吗?全局声明这样做没有意义,或者它会破坏某些东西吗?Python不知道全局const
不会变化,所以它不能只是复制它,或者至少不能不做Python的C实现所不能做的其他优化。所以我想知道的底线是,如果全局变量是只读的,那么它是否会在局部变量中添加一个条目:“任何东西,即使是另一个线程或调试器,都可以在循环执行时修改该值”-非常感谢这正是我想要的:)至于表现,我打赌这种差异会对阿伏伽德罗产生几千年的影响。D:为什么每当有人问到关于表现的问题时,人们总是向你抛出库恩的话,好像你不知道,而不是看着这个问题-嘿男生表演不是禁忌,我们是成年人;)实际上,我认为您会发现您的代码抛出TypeError:integer参数expected,get float
或overflowerrror:Python int太大,无法转换为C long
,具体取决于AVOGADRO
的值。升级到Python3并使用range()
,您可能会让它正常工作。
>>> 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