【问题标题】:Why is @property slower that an attribute while bytecode is the same为什么@property 在字节码相同的情况下比属性慢
【发布时间】:2019-11-25 02:26:14
【问题描述】:

考虑一下这段代码:

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

为什么?

【问题讨论】:

    标签: python performance properties cpython python-internals


    【解决方案1】:

    属性作为函数调用执行,而属性查找只是哈希表(字典)查找。所以是的,这总是会更慢。

    LOAD_ATTR 字节码在这里不是一个固定时间的操作。您缺少的是 LOAD_ATTR 将属性查找委托给对象类型;通过触发 C 代码:

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

    有关描述符的更多信息,请参阅Descriptor HOWTO,以及 Python 数据模型文档的 Invoking Descriptors section

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

    class Foo:
        def __init__(self):
            self.bar = 42
    
    f = Foo()
    print(f.bar)  # 42
    
    Foo.bar = property(lambda self: 81)
    print(f.bar)  # 81
    

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

    【讨论】:

    • “属性作为函数调用执行” - 我在字节码中看不到它
    • @ivaigult:不,你不会。
    • @ivaigult:字节码是静态的,但属性不是静态的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多