为什么 @属性 在字节码相同的情况下比属性慢

Why is @property slower that an attribute while bytecode is the same

考虑这段代码:

import timeit
import dis

class Bob(object):
    __slots__ = "_a",

    def __init__(self):
        self._a = "a"

    @property
    def a_prop(self):
        return self._a

bob = Bob()

def return_attribute():
    return bob._a

def return_property():
    return bob.a_prop

print(dis.dis(return_attribute))
print(dis.dis(return_property))

print("attribute:")
print(timeit.timeit("return_attribute()",
                    setup="from __main__ import return_attribute", number=1000000))
print("@property:")
print(timeit.timeit("return_property()",
                    setup="from __main__ import return_property", number=1000000))

很容易看出return_attributereturn_property得到相同的字节码:

 17           0 LOAD_GLOBAL              0 (bob)
              3 LOAD_ATTR                1 (_a)
              6 RETURN_VALUE        
None
 20           0 LOAD_GLOBAL              0 (bob)
              3 LOAD_ATTR                1 (a_prop)
              6 RETURN_VALUE        
None

但是,时间不同:

attribute:
0.106526851654
@property:
0.210631132126

为什么?

A 属性 作为 函数调用 执行,而属性查找只是散列 table(字典)查找。所以是的,那总是会更慢。

这里的LOAD_ATTR字节码不是fixed-time操作。您缺少的是 LOAD_ATTR 将属性查找委托给对象类型 ;通过触发 C 代码:

一个property对象是一个数据描述符;它不仅实现了 __get__,还实现了 __set____delete__ 方法。使用实例在 property 上调用 __get__ 会导致 property 对象调用已注册的 getter 函数。

请参阅 Python 数据模型文档的 Descriptor HOWTO for more information on descriptors, as well as the Invoking Descriptors section

字节码 没有区别 因为它不是由 LOAD_ATTR 字节码来决定属性是 属性 还是常规属性. Python 是一种动态语言,编译器无法预先知道 所访问的属性是否为 属性。您可以随时更改 class :

class Foo:
    def __init__(self):
        self.bar = 42

f = Foo()
print(f.bar)  # 42

Foo.bar = property(lambda self: 81)
print(f.bar)  # 81

在上面的示例中,当您从 bar 名称开始时,它仅作为 class Foof 实例中的一个属性存在,通过添加 Foo.bar property 对象我们拦截了名称 bar 的查找过程,因为 property 是一个数据描述符,因此可以覆盖任何实例查找。 但是Python无法提前知道这一点,因此无法为属性查找提供不同的字节码。例如,Foo.bar 赋值可能发生在完全不相关的模块中。