Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Python中__slots__的目的是什么-尤其是关于何时以及何时不使用它的目的?

  ask by Jeb translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
167 views
Welcome To Ask or Share your Answers For Others

1 Answer

In Python, what is the purpose of __slots__ and what are the cases one should avoid this? 在Python中, __slots__的目的是什么?在什么情况下应避免这种情况?

TLDR: TLDR:

The special attribute __slots__ allows you to explicitly state which instance attributes you expect your object instances to have, with the expected results: 特殊属性__slots__允许您显式说明您希望对象实例具有哪些实例属性,并具有预期的结果:

  1. faster attribute access. 更快的属性访问。
  2. space savings in memory. 节省内存空间

The space savings is from 节省的空间来自

  1. Storing value references in slots instead of __dict__ . 将值引用存储在插槽中而不是__dict__
  2. Denying __dict__ and __weakref__ creation if parent classes deny them and you declare __slots__ . 如果父类拒绝__dict____weakref__而您声明__slots__拒绝创建。

Quick Caveats 快速警告

Small caveat, you should only declare a particular slot one time in an inheritance tree. 请注意,您只应在继承树中一次声明一个特定的插槽。 For example: 例如:

class Base:
    __slots__ = 'foo', 'bar'

class Right(Base):
    __slots__ = 'baz', 

class Wrong(Base):
    __slots__ = 'foo', 'bar', 'baz'        # redundant foo and bar

Python doesn't object when you get this wrong (it probably should), problems might not otherwise manifest, but your objects will take up more space than they otherwise should. 遇到错误时,Python不会反对(它应该会),否则问题可能不会显现出来,但是您的对象将比原先占用更多的空间。

>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(64, 80)

The biggest caveat is for multiple inheritance - multiple "parent classes with nonempty slots" cannot be combined. 最大的警告是多重继承-无法合并多个“具有非空插槽的父类”。

To accommodate this restriction, follow best practices: Factor out all but one or all parents' abstraction which their concrete class respectively and your new concrete class collectively will inherit from - giving the abstraction(s) empty slots (just like abstract base classes in the standard library). 为适应此限制,请遵循最佳实践:排除其父母和您的新具体类将分别继承的一个或所有父类的所有抽象-给这些抽象空的位置(就像在父类中的抽象基类一样)标准库)。

See section on multiple inheritance below for an example. 有关示例,请参见下面有关多重继承的部分。

Requirements: 要求:

  • To have attributes named in __slots__ to actually be stored in slots instead of a __dict__ , a class must inherit from object . 要使在__slots__命名的属性实际上存储在插槽中而不是在__dict__存储,类必须从object继承。

  • To prevent the creation of a __dict__ , you must inherit from object and all classes in the inheritance must declare __slots__ and none of them can have a '__dict__' entry. 为了防止创建__dict__ ,您必须从object继承,并且继承中的所有类都必须声明__slots__并且它们都不能具有'__dict__'条目。

There are a lot of details if you wish to keep reading. 如果您想继续阅读,有很多细节。

Why use __slots__ : Faster attribute access. 为什么使用__slots__ :更快的属性访问。

The creator of Python, Guido van Rossum, states that he actually created __slots__ for faster attribute access. Python的创建者Guido van Rossum ,他实际上创建了__slots__以便更快地访问属性。

It is trivial to demonstrate measurably significant faster access: 证明可观的显着更快访问是微不足道的:

import timeit

class Foo(object): __slots__ = 'foo',

class Bar(object): pass

slotted = Foo()
not_slotted = Bar()

def get_set_delete_fn(obj):
    def get_set_delete():
        obj.foo = 'foo'
        obj.foo
        del obj.foo
    return get_set_delete

and

>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085

The slotted access is almost 30% faster in Python 3.5 on Ubuntu. 在Ubuntu 3.5上的Python 3.5中,插槽式访问的速度几乎快了30%。

>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342

In Python 2 on Windows I have measured it about 15% faster. 在Windows上的Python 2中,我测得的速度要快15%。

Why use __slots__ : Memory Savings 为什么使用__slots__ :节省内存

Another purpose of __slots__ is to reduce the space in memory that each object instance takes up. __slots__另一个目的是减少每个对象实例占用的内存空间。

My own contribution to the documentation clearly states the reasons behind this : 我自己对文档的贡献清楚地说明了背后的原因

The space saved over using __dict__ can be significant. 使用__dict__节省的空间可能很大。

SQLAlchemy attributes a lot of memory savings to __slots__ . SQLAlchemy将大量内存节省归因__slots__

To verify this, using the Anaconda distribution of Python 2.7 on Ubuntu Linux, with guppy.hpy (aka heapy) and sys.getsizeof , the size of a class instance without __slots__ declared, and nothing else, is 64 bytes. 为了验证这一点,请在Ubuntu Linux上使用Python 2.7的Anaconda发行版,并带有guppy.hpy (又称堆)和sys.getsizeof ,不声明__slots__且没有其他声明的类实例的大小为64字节。 That does not include the __dict__ . 包括__dict__ Thank you Python for lazy evaluation again, the __dict__ is apparently not called into existence until it is referenced, but classes without data are usually useless. 再次感谢Python的惰性求值,显然在引用__dict__之前,它并不存在,但是没有数据的类通常是无用的。 When called into existence, the __dict__ attribute is a minimum of 280 bytes additionally. 当被调用时, __dict__属性至少要最少280个字节。

In contrast, a class instance with __slots__ declared to be () (no data) is only 16 bytes, and 56 total bytes with one item in slots, 64 with two. 相反,声明为() (无数据)的__slots__的类实例只有16个字节,并且插槽中有一项的总字节数为56个,插槽中有两项的64个字节。

For 64 bit Python, I illustrate the memory consumption in bytes in Python 2.7 and 3.6, for __slots__ and __dict__ (no slots defined) for each point where the dict grows in 3.6 (except for 0, 1, and 2 attributes): 对于64位Python,我将说明dict在3.6中增长的每个点的__slots____dict__ (未定义插槽)的内存消耗(以字节为单位)以python 2.7和3.6为单位(0、1和2属性除外):

       Python 2.7             Python 3.6
attrs  __slots__  __dict__*   __slots__  __dict__* | *(no slots defined)
none   16         56 + 272?   16         56 + 112? | ?if __dict__ referenced
one    48         56 + 272    48         56 + 112
two    56         56 + 272    56         56 + 112
six    88         56 + 1040   88         56 + 152
11     128        56 + 1040   128        56 + 240
22     216        56 + 3344   216        56 + 408     
43     384        56 + 3344   384        56 + 752

So, in spite of smaller dicts in Python 3, we see how nicely __slots__ scale for instances to save us memory, and that is a major reason you would want to use __slots__ . 因此,尽管Python 3中的指令较小,但我们看到__slots__可以很好地扩展实例以节省内存,这是您想使用__slots__主要原因。

Just for completeness of my notes, note that there is a one-time cost per slot in the class's namespace of 64 bytes in Python 2, and 72 bytes in Python 3, because slots use data descriptors like properties, called "members". 仅出于我的注意事项的完整性,请注意,在类的名称空间中,每个插槽的一次性成本为Python 2中64字节,而在Python 3中为72字节,因为插槽使用数据描述符(如属性)称为“成员”。

>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72

Demonstration of __slots__ : __slots__演示:

To deny the creation of a __dict__ , you must subclass object : 要拒绝创建__dict__ ,必须子类化object

class Base(object): 
    __slots__ = ()

now: 现在:

>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'

Or subclass another class that defines __slots__ 或子类化另一个定义__slots__

class Child(Base):
    __slots__ = ('a',)

and now: 现在:

c = Child()
c.a = 'a'

but: 但:

>>> c.b = 'b'
Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'

To allow __dict__ creation while subclassing slotted objects, just add '__dict__' to the __slots__ (note that slots are ordered, and you shouldn't repeat slots that are already in parent classes): 要在创建插槽对象的子类时创建__dict__ ,只需在__slots__添加'__dict__' (请注意,插槽是有序的,并且您不应重复父类中已经存在的插槽):

class SlottedWithDict(Child): 
    __slots__ = ('__dict__', 'b')

swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'

and

>>> swd.__dict__
{'c': 'c'}

Or you don't even need to declare __slots__ in your subclass, and you will still use slots from the parents, but not restrict the creation of a __dict__ : 或者甚至不需要在子类中声明__s


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...