python - 在 Python 中创建类的开销 : Exact same code using class twice as slow as native DS?

标签 python performance function class

我使用所有列表函数在 Python 中创建了一个 Stack 类作为练习。例如,Stack.push() 就是 list.append(),Stack.pop() 就是 list.pop(),Stack.isEmpty() 就是 list == [ ]。

我正在使用我的 Stack 类来实现一个十进制到二进制的转换器,我注意到即使这两个函数完全等同于我的 Stack 类对 push()、pop() 和 isEmpty() 的包装,使用 Stack 类的实现比使用 Python 列表的实现慢两倍。

那是因为在 Python 中使用类总是有固有的开销吗?如果是这样,从技术上讲(“幕后”)开销来自哪里?最后,如果开销如此之大,除非绝对必要,否则最好不要使用类?

def dectobin1(num):
    s = Stack()
    while num > 0:
        s.push(num % 2)
        num = num // 2
    binnum = ''
    while not s.isEmpty():
        binnum = binnum + str(s.pop())
    return binnum

def dectobin2(num):
    l = []
    while num > 0:
        l.append(num % 2)
        num = num // 2
    binnum = ''
    while not l == []:
        binnum = binnum + str(l.pop())
    return binnum


t1 = Timer('dectobin1(255)', 'from __main__ import dectobin1')
print(t1.timeit(number = 1000))

0.0211110115051

t2 = Timer('dectobin2(255)', 'from __main__ import dectobin2')
print(t2.timeit(number = 1000))

0.0094211101532

最佳答案

首先,警告:函数调用很少会限制您的速度。这通常是不必要的微优化。只这样做,如果它实际上限制了你的表现。之前做一些好的分析,看看是否有更好的优化方法。

确保您不会因为这个微小的性能调整而牺牲易读性!

Python 中的类有点像 hack。

它的工作方式是每个对象都有一个 __dict__ 字段(字典),其中包含该对象包含的所有属性。此外,每个对象都有一个 __class__ 对象,它又包含一个 __dict__ 字段(又是一个字典),其中包含所有类属性。

例如,看看这个:

>>> class X(): # I know this is an old-style class declaration, but this causes far less clutter for this demonstration
...     def y(self):
...             pass
...
>>> x = X()
>>> x.__class__.__dict__
{'y': <function y at 0x6ffffe29938>, '__module__': '__main__', '__doc__': None}

如果您动态定义一个函数(所以不是在类声明中而是在对象创建之后),该函数不会转到 x.__class__.__dict__ 而是转到 x.__dict__

还有两个字典保存当前函数可访问的所有变量。 globals()locals() 包括所有全局和局部变量。

那么现在让我们说,你有一个 x 类的对象 X 具有函数 yz在类声明中声明,第二个函数 z 是动态定义的。假设对象 x 是在全局空间中定义的。 此外,为了比较,有两个函数 flocal() 定义在局部空间,fglobal() 定义在全局空间。

现在我将展示调用这些函数中的每一个会发生什么:

flocal():
    locals()["flocal"]()

fglobal():
    locals()["fglobal"] -> not found
    globals()["fglobal"]()

x.y():
    locals()["x"] -> not found
    globals()["x"].__dict__["y"] -> not found, because y is in class space
                  .__class__.__dict__["y"]()

x.z():
    locals()["x"] -> not found
    globals()["x"].__dict__["z"]() -> found in object dict, ignoring z() in class space

如您所见,类空间方法需要更多时间来查找,对象空间方法也很慢。最快的选择是本地函数。

但是您可以在不牺牲类(class)的情况下解决这个问题。比方说,x.y() 被调用了很多,需要优化。

class X():
    def y(self):
        pass

x = X()
for i in range(100000):
    x.y() # slow

y = x.y # move the function lookup outside of loop
for i in range(100000):
    y() # faster

对象的成员变量也会发生类似的事情。它们也比局部变量慢。如果您调用一个函数或使用一个对象中的成员变量,而该对象是另一个对象的成员变量,则效果也会增加。例如

a.b.c.d.e.f()

会慢一点,因为每个点都需要另一个字典查找。

官方 Python 性能指南建议避免在代码的性能关键部分出现点: https://wiki.python.org/moin/PythonSpeed/PerformanceTips

关于python - 在 Python 中创建类的开销 : Exact same code using class twice as slow as native DS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41781048/

相关文章:

python - 在 Python 中计算无重复的排列

python正则表达式在字符的最后两次出现之间保留文本

php - 在 jquery 函数中使用 HTML/PHP 代码

c - 使用 C 从宏函数的参数中获取 OPERATOR

python - 将 Python 模块作为参数传递是一个好习惯吗

Python3 在嵌套字典和列表中按值搜索,然后获取其他最近的键/值对

c# - SQL 查询性能 - 如果有的话,哪种方法更好?

java - 并行运行两个昂贵的数据库调用

performance - yslow 只是说 28 个组件未添加到 CDN 我如何获得完整的组件详细信息

Python:从子函数/嵌套函数更改函数中的变量?