为什么使用__slots__会减慢此代码的速度?

Ayu*_*har 1 python performance time class slots

我读过《 __slots__用法》吗?__slots__在Python 中使用实际上可以节省时间。但是,当我尝试使用花费时间时datetime,结果却相反。

import datetime as t

class A():
    def __init__(self,x,y):
        self.x = x
        self.y = y

t1 = t.datetime.now()
a = A(1,2)
t2 = t.datetime.now()
print(t2-t1)
Run Code Online (Sandbox Code Playgroud)

...给出了输出:0:00:00.000011 并使用插槽:

import datetime as t

class A():
    __slots__ = 'x','y'
    def __init__(self,x,y):
        self.x = x
        self.y = y

t1 = t.datetime.now()
a = A(1,2)
t2 = t.datetime.now()
print(t2-t1)
Run Code Online (Sandbox Code Playgroud)

...给出了输出: 0:00:00.000021

使用插槽实际上花费了更长的时间。为什么我们需要使用__slots__呢?

Sha*_*ger 6

__slots__可以节省时间(取决于Python版本),但这通常不是您使用的时间。它真正节省的是内存。__dict__您可以将属性直接存储在支持该对象的C结构中,而不是为每个实例分配一个相当大的大小,并且类本身可以存储查找表的单个副本,该副本从名称映射到每个属性的结构偏移量。即使在具有键共享字典的现代Py3 x64上,键共享仍为96字节,__dict__其中该类具有单个实例属性,在对象结构本身的56个字节之上。

通过使用__slots__,可以消除指向__dict____weakref__属性的指针的16个字节,并__dict__完全消除。

为了在Py 3.5上进行比较:

>>> class Foo:
...    def __init__(self, x): self.x = x
...
>>> sys.getsizeof(Foo(1)) + sys.getsizeof(Foo(1).__dict__)
152
>>> class Foo:
...    __slots__ = 'x',
...    def __init__(self, x): self.x = x
...
>>> sys.getsizeof(Foo(1))  # With __slots__, doesn't have __dict__ at all
48
Run Code Online (Sandbox Code Playgroud)

这样每个实例可以节省100多个字节。在Py2(无密钥共享字典)上,节省的费用更大。

因此,__slots__通常这并不是更快(通常非常相似),但是如果您要创建数百万个实例,则为每个实例节省100+ B可能有助于您将代码保存在缓存,RAM等中,而不是耗尽内存并分页出一半数据以进行交换。

如其他答案所述,您实际上从未访问过属性,因此根本就没有基准测试插槽访问权限,这就是为什么看不到任何区别的原因。使用ipython3 %%timeit魔术,我发现x重复加载给定实例的属性在插入时隙时大约快15%(有33.5 ns __slots__对比无对象39.2 ns),但这仅在微基准测试中很明显。在实际代码中(在实际工作中所做的不仅仅是属性查找而已)还很少。重要的是,将内存使用量减少2-3倍是更大的收益。


Bło*_*tek 5

  1. 您引用的文章说使用插槽提供了更快的属性访问 -您测试了对象创建的时间,并且从未访问过对象的属性。
  2. 测试单个操作在统计上没有意义-测量例如100000次操作的时间。