Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:OOP开销?_Python_Performance_Oop - Fatal编程技术网

Python:OOP开销?

Python:OOP开销?,python,performance,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.93229103

我一直在开发一个实时应用程序,并注意到一些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)正在进行动态查找,以分派所有调用、索引等。动态查找允许语言具有很大的灵活性,但要以性能为代价。使用“方法包装器”时,至少会发生以下情况:

  • 查找
    mwp.id
    -它恰好是一个方法,但它也只是一个分配给属性的对象,必须像其他对象一样进行查找
  • 调用
    mwp.id()
  • 在方法内部,查找
    self.\u数据
  • 查找
    自身数据的
    \uu获取项目
    。\u数据
  • 调用
    \uuuu getitem\uuuuu
    (这至少是一个C函数,但您仍然需要通过所有这些动态查找才能到达此处)
相比之下,“Dict访问”测试用例只需查找
\uu getitem\uu
,然后调用它

正如Matteo Italia在评论中指出的,这是特定于实现的。在Python生态系统中,现在您也有PyPy(使用JIT和运行时优化)、Cython(编译到C,带有可选的静态类型注释等)、Nuitka(编译为C++,应该按原样取代码)和多个其他实现。
在CPython上的“纯”Python中优化这些查找的一种方法是获取对对象的直接引用,并将它们分配给循环外部的局部变量,然后在循环内部使用局部变量。这是一个优化,可能会以混乱代码和/或破坏封装为代价。

+1,这是一个有趣的问题,但我认为很少有必要过多地考虑CPython的性能-一旦你使用CPython,你已经失去了性能训练。顺便说一句,使用PyPy Dict,方法和属性包装器访问占用的时间是相同的(这大约是我机器上Cpyton的Dict访问时间的一半),属性访问速度大约是Cpyton的Dict的6倍(即比Cpyton的Dict快12倍)。感谢Jason的输入。除了一个巨蟒迷,我不得不承认,当我注意到这一点时,我有点失望。但是,是的,巨大的灵活性是有代价的。