Python:OOP 开销?

标签 python performance oop

我一直在开发一个实时应用程序,并注意到一些 OOP 设计模式在 Python 中引入了难以置信的开销(使用 2.7.5 进行了测试)。

直截了当,当字典被另一个对象封装时,为什么简单的字典值访问器方法花费将近 5 倍的时间?

例如,运行下面的代码,我得到:

Dict Access: 0.167706012726
Attribute Access: 0.191128969193
Method Wrapper Access: 0.711422920227
Property Wrapper Access: 0.932291030884

可执行代码:

class Wrapper(object):
    def __init__(self, data):
        self._data = data

    @property
    def id(self):
        return self._data['id']

    @property
    def name(self):
        return self._data['name']

    @property
    def score(self):
        return self._data['score']


class MethodWrapper(object):
    def __init__(self, data):
        self._data = data

    def id(self):
        return self._data['id']

    def name(self):
        return self._data['name']

    def score(self):
        return self._data['score']


class Raw(object):
    def __init__(self, id, name, score):
        self.id = id
        self.name = name
        self.score = score


data = {'id': 1234, 'name': 'john', 'score': 90}
wp = Wrapper(data)
mwp = MethodWrapper(data)
obj = Raw(data['id'], data['name'], data['score'])


def dict_access():
    for _ in xrange(100):
        uid = data['id']
        name = data['name']
        score = data['score']


def method_wrapper_access():
    for _ in xrange(100):
        uid = mwp.id()
        name = mwp.name()
        score = mwp.score()


def property_wrapper_access():
    for _ in xrange(100):
        uid = wp.id
        name = wp.name
        score = wp.score


def object_access():
    for _ in xrange(100):
        uid = obj.id
        name = obj.name
        score = obj.score


import timeit
print 'Dict Access:', timeit.timeit("dict_access()", setup="from __main__ import dict_access", number=10000)
print 'Attribute Access:', timeit.timeit("object_access()", setup="from __main__ import object_access", number=10000)
print 'Method Wrapper Access:', timeit.timeit("method_wrapper_access()", setup="from __main__ import method_wrapper_access", number=10000)
print 'Property Wrapper Access:', timeit.timeit("property_wrapper_access()", setup="from __main__ import property_wrapper_access", number=10000)

最佳答案

这是因为 Python 解释器 (CPython) 正在执行动态查找以分派(dispatch)您的所有调用、索引等。动态查找在语言中提供了很大的灵 active ,但以性能成本为代价。当您使用“方法包装器”时,这(至少)正在发生:

  • 查找 mwp.id - 它恰好是一个方法,但它也只是一个分配给属性的对象,必须像查找其他对象一样查找
  • 调用 mwp.id()
  • 在方法内部,查找self._data
  • 查找self._data__getitem__
  • 调用 __getitem__(这至少是一个 C 函数,但您仍然必须通过所有这些动态查找才能到达此处)

相比之下,您的“Dict Access”测试用例只需查找 __getitem__ 然后调用它。

正如 Matteo Italia 在评论中指出的那样,这是特定于实现的。在 Python 生态系统中,现在你还有 PyPy(使用 JIT 和运行时优化)、Cython(编译为 C,带有可选的静态类型注释等)、Nuitka(编译为 C++,应该按原样获取代码),以及多个其他实现。

在 CPython 上的“纯”Python 中优化这些查找的一种方法是获取对对象的直接引用并将它们分配给循环外的局部变量,然后在循环内使用局部变量。这种优化可能会导致代码困惑和/或破坏封装。

关于Python:OOP 开销?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25335862/

相关文章:

java - 创建一个可以转换不同类列表的函数

Python/Zeep/SOAP代理问题(我认为)

python - 使用 Flask-SQLAlchemy 查询

python - 从两点之间的 numpy 数组获取值

python - 有效地计算边界自适应邻域平均值

java - 确保对象构建前或构建时的有效性

python - 从通过 Python 原始套接字接收的 ICMP 消息中读取 TTL

sql-server - 为什么连接两个表变量会显着增加 SQL Server 2012 中的执行时间

C++ 切换表性能

c# - 为什么静态构造函数没有任何参数?