首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python: OOP开销?

Python: OOP开销?
EN

Stack Overflow用户
提问于 2014-08-16 00:20:11
回答 1查看 511关注 0票数 4

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

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

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

代码语言:javascript
复制
Dict Access: 0.167706012726
Attribute Access: 0.191128969193
Method Wrapper Access: 0.711422920227
Property Wrapper Access: 0.932291030884

可执行代码:

代码语言:javascript
复制
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)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-16 00:29:29

这是因为Python解释器(CPython)用于分派所有调用、索引等的动态查找。动态查找在语言中允许很大的灵活性,但需要付出性能代价。当您使用“方法包装器”时,(至少)会发生这样的情况:

  • 查找mwp.id --它碰巧是一个方法,但它也只是一个分配给属性的对象,必须像任何其他对象一样查找
  • 调用mwp.id()
  • 在方法中,查找self._data
  • 查找__getitem__ of self._data
  • 调用__getitem__ (至少这将是一个C函数,但您仍然需要经过所有的动态查找才能到达这里)

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

正如意大利马泰奥在一份评论中指出的那样,这是具体的实施。在Python生态系统中,您现在还拥有PyPy (使用JIT和运行时优化)、Cython (编译为C、带有可选的静态类型注释等)、Nuitka (将代码编译为-is)以及多个其他实现。

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

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25335862

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档