Python中访问类变量的性能

Python中访问类变量的性能,python,global-variables,scope,Python,Global Variables,Scope,我想知道在使用以下方法访问同一类的方法内部的类变量(dict)时,性能是否有任何差异: self.class_variable_dict.add(some_key, some_value) 及 显然,只要没有同名的实例变量,这两种方法都可以工作,但是有什么原因/用例我们应该选择一种而不是另一种吗?通过ClassName而不是通过self访问它会稍微快一点,因为如果您通过self访问它,它必须首先检查实例名称空间。但我并不认为这两者之间的差别有多大,除非你有分析信息表明这一点 因此,我建议使用你

我想知道在使用以下方法访问同一类的方法内部的类变量(dict)时,性能是否有任何差异:

self.class_variable_dict.add(some_key, some_value)


显然,只要没有同名的实例变量,这两种方法都可以工作,但是有什么原因/用例我们应该选择一种而不是另一种吗?

通过
ClassName
而不是通过
self
访问它会稍微快一点,因为如果您通过
self
访问它,它必须首先检查实例名称空间。但我并不认为这两者之间的差别有多大,除非你有分析信息表明这一点

因此,我建议使用你认为作为人类更容易阅读/理解的方法

语义上,只有当
class\u variable\u dict
变量被隐藏在某个地方时,它们才会有所不同——特别是,如果(a)
self
定义了一个同名变量;或者(b)
self
ClassName
的子类的一个实例,并且该子类(或者它的一个基仍然是
ClassName
的子类)定义了一个同名变量。如果两者都不是真的,那么它们在语义上应该是相同的

编辑:

德尔南有一个很好的观点:有一些因素可能会使两者更快。我坚持我的主张,除非是在一个非常非常紧密的循环中,否则差异将是微不足道的。为了测试它,我创建了我能想到的最紧密的循环,并用
timeit
计时。结果如下:

  • 通过类变量访问:20.226秒
  • 通过仪表变量访问:23.121秒
从几次运行来看,误差条大约为1秒——也就是说,这是一个统计上的显著差异,但可能不值得担心。以下是我的测试代码:

import timeit

setup='''
class A:
    var = {}
    def f1(self):
        x = A.var
    def f2(self):
        x = self.var

a = A()
'''
print 'access via class var: %.3f' % timeit.timeit('a.f1()', setup=setup, number=100000000)
print 'access via inst var: %.3f' % timeit.timeit('a.f2()', setup=setup, number=100000000)

让我们看看不同的选项都做了什么

In [1]: class Foo:
   ...:     bar = {}
   ...:     

In [2]: import dis
In [3]: dis.dis(lambda: Foo.bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 LOAD_ATTR                1 (bar) 
              6 LOAD_ATTR                2 (add) 
              9 LOAD_CONST               1 (1) 
             12 LOAD_CONST               2 (2) 
             15 CALL_FUNCTION            2 
             18 RETURN_VALUE         

In [4]: dis.dis(lambda: Foo().bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 CALL_FUNCTION            0 
              6 LOAD_ATTR                1 (bar) 
              9 LOAD_ATTR                2 (add) 
             12 LOAD_CONST               1 (1) 
             15 LOAD_CONST               2 (2) 
             18 CALL_FUNCTION            2 
             21 RETURN_VALUE 
从中可以看出,除了在第二种情况下创建对象之外,这两种样式都生成相同的字节码


另一方面,这并不重要。用最精确的方式表达你的目标。只有在速度重要时才进行优化

我建议只使用
ClassName.dict\u value[key]=value


这里也可以看到同样的问题,我在@Edward Loper test的基础上发布了一个更广泛的测试,包括比类变量更快的模块变量。

如果你真的关心速度,那么显然你根本不把它放在类中,而是有一个模块全局_classname _dict_值,除了有人可以在您的模块作用域之外访问dict_值之外,没有其他好处。@senderle:字节码只是在实例化对象时有所不同。好的,谢谢您解决这个问题。但我仍然认为需要注意的是,相同的字节码并不一定会产生相同的性能。在这种情况下,LOAD_ATTR是一条字节码指令,但它可以更快或更慢,这取决于具体情况——相反,同一组cpu指令可能更快或更慢,这取决于是否存在缓存未命中。oth,查找
self
会更快,因为它是本地指令,因此可以从C数组访问,而
ClassName
是一个全局变量,必须在dict中查找。除了评测之外,没有办法知道哪个更快!
In [1]: class Foo:
   ...:     bar = {}
   ...:     

In [2]: import dis
In [3]: dis.dis(lambda: Foo.bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 LOAD_ATTR                1 (bar) 
              6 LOAD_ATTR                2 (add) 
              9 LOAD_CONST               1 (1) 
             12 LOAD_CONST               2 (2) 
             15 CALL_FUNCTION            2 
             18 RETURN_VALUE         

In [4]: dis.dis(lambda: Foo().bar.add(1,2))
  1           0 LOAD_GLOBAL              0 (Foo) 
              3 CALL_FUNCTION            0 
              6 LOAD_ATTR                1 (bar) 
              9 LOAD_ATTR                2 (add) 
             12 LOAD_CONST               1 (1) 
             15 LOAD_CONST               2 (2) 
             18 CALL_FUNCTION            2 
             21 RETURN_VALUE