我想知道使用以下命令访问同一类的方法中的类变量(字典(时性能是否有任何差异:
self.class_variable_dict.add(some_key, some_value)
和
ClassName.class_variable_dict.add(some_key, some_value)
显然,只要没有同名的实例变量,两者都可以工作,但是有什么理由/用例我们应该更喜欢一个而不是另一个吗?
通过 ClassName
而不是通过 self
访问它会稍微快一些,因为如果您通过 self
访问它,它必须首先检查实例命名空间。 但我不认为差异会很大,除非你有分析信息表明它是。
所以我建议使用你认为更容易阅读/理解的人类。
从语义上讲,只有当class_variable_dict
变量在某处被阴影时,它们才会有所不同——特别是,如果(a(self
定义了同名的变量;或者(b(self
是ClassName
子类的实例,并且该子类(或其基础之一仍然是ClassName
的子类(定义了同名的变量。 如果这些都不是真的,那么它们在语义上应该是相同的。
编辑:
Delnam有一个很好的观点:有一些因素可能会使两者更快。 我坚持我的断言,除非它处于一个非常非常紧密的循环中,否则差异将是微不足道的。 为了测试它,我创建了我能想到的最紧密的循环,并用timeit
计时。 以下是结果:
- 通过类 var 访问:20.226 秒
- 通过 INST VaR 访问: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_value[key] = value
一起去
Python 中的实例变量与.class变量
另请参阅此处相同的问题,其中我发布了基于@Edward Loper 测试的更广泛测试,包括模块变量,这些变量甚至比类变量更快。