- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
__slots__
的目的是什么?在 Python 中——尤其是关于我什么时候想使用它,什么时候不想使用它?
最佳答案
In Python, what is the purpose of
__slots__
and what are the cases one should avoid this?
__slots__
允许您明确说明您希望对象实例具有哪些实例属性,并具有预期结果:
__dict__
. __dict__
和 __weakref__
如果父类拒绝它们并且您声明 __slots__
,则创建. class Base:
__slots__ = 'foo', 'bar'
class Right(Base):
__slots__ = 'baz',
class Wrong(Base):
__slots__ = 'foo', 'bar', 'baz' # redundant foo and bar
当您弄错时,Python 不会反对(它可能应该),问题可能不会以其他方式出现,但是您的对象将占用更多的空间。 python 3.8:
>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(56, 72)
这是因为 Base 的插槽描述符有一个与 Wrong 的插槽分开的插槽。这通常不应该出现,但它可以:
>>> w = Wrong()
>>> w.foo = 'foo'
>>> Base.foo.__get__(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo
>>> Wrong.foo.__get__(w)
'foo'
最大的警告是多重继承——不能组合多个“具有非空槽的父类”。
__slots__
中命名的属性实际上存储在插槽中而不是 __dict__
,一个类必须继承自 object
(在 Python 3 中是自动的,但在 Python 2 中必须是显式的)。__dict__
,您必须继承自 object
并且继承中的所有类都必须声明 __slots__
并且他们都不能拥有 '__dict__'
入口。__slots__
:更快的属性访问。
__slots__
更快的属性访问。
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
和
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
在 Ubuntu 上的 Python 3.5 中,插槽访问速度快了近 30%。
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
在 Windows 上的 Python 2 中,我测得它快了大约 15%。
__slots__
: 内存节省
__slots__
的另一个目的就是减少每个对象实例占用的内存空间。
The space saved over using
__dict__
can be significant.
__slots__
.
guppy.hpy
(又名肥大)和
sys.getsizeof
, 没有
__slots__
的类实例的大小声明,没有别的,是 64 字节。这不包括
__dict__
.再次感谢 Python 的懒惰评估,
__dict__
显然在被引用之前不会被调用,但是没有数据的类通常是无用的。当被调用时,
__dict__
属性至少为 280 字节。
__slots__
的类实例声明为
()
(无数据)只有 16 个字节,插槽中有一个项目总共 56 个字节,两个有 64 个字节。
__slots__
和
__dict__
(未定义插槽)对于 dict 在 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
所以,尽管 Python 3 中的字典更小,但我们看到
__slots__
多么好。扩展实例以节省我们的内存,这是您希望使用
__slots__
的主要原因.
>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72
演示
__slots__
:
__dict__
,您必须子类化
object
.一切都是子类
object
在 Python 3 中,但在 Python 2 中,您必须明确:
class Base(object):
__slots__ = ()
现在:
>>> 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'
或者子类化另一个定义
__slots__
的类
class Child(Base):
__slots__ = ('a',)
现在:
c = Child()
c.a = 'a'
但:
>>> 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'
允许
__dict__
在子类化槽对象时创建,只需添加
'__dict__'
到
__slots__
(注意插槽是有序的,你不应该重复已经在父类中的插槽):
class SlottedWithDict(Child):
__slots__ = ('__dict__', 'b')
swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'
和
>>> swd.__dict__
{'c': 'c'}
或者你甚至不需要声明
__slots__
在您的子类中,您仍将使用来自父类的插槽,但不限制创建
__dict__
:
class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'
和:
>>> ns.__dict__
{'b': 'b'}
然而,
__slots__
可能会导致多重继承问题:
class BaseA(object):
__slots__ = ('a',)
class BaseB(object):
__slots__ = ('b',)
因为从具有两个非空插槽的父类创建子类失败:
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
如果你遇到这个问题,你可以删除
__slots__
来自 parent ,或者如果你可以控制 parent ,给他们空位,或者重构抽象:
from abc import ABC
class AbstractA(ABC):
__slots__ = ()
class BaseA(AbstractA):
__slots__ = ('a',)
class AbstractB(ABC):
__slots__ = ()
class BaseB(AbstractB):
__slots__ = ('b',)
class Child(AbstractA, AbstractB):
__slots__ = ('a', 'b')
c = Child() # no problem!
添加
'__dict__'
至
__slots__
获得动态分配:
class Foo(object):
__slots__ = 'bar', 'baz', '__dict__'
现在:
>>> foo = Foo()
>>> foo.boink = 'boink'
所以与
'__dict__'
在插槽中,我们失去了一些大小优势,但具有动态分配的优势,并且仍然具有我们期望的名称的插槽。
__slots__
时,你会得到同样的语义。 - 在
__slots__
中的名称指向槽值,而任何其他值都放在实例的
__dict__
中.
__slots__
因为您希望能够即时添加属性实际上并不是一个很好的理由 - 只需添加
"__dict__"
给您的
__slots__
如果这是必需的。
__weakref__
至
__slots__
如果您需要该功能,请明确表示。
from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
"""MyNT is an immutable and lightweight object"""
__slots__ = ()
用法:
>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'
尝试分配一个意外的属性会引发
AttributeError
因为我们阻止了
__dict__
的创建:
>>> nt.quux = 'quux'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'
您可以允许
__dict__
离开创造
__slots__ = ()
,但不能使用非空
__slots__
元组的子类型。
class Foo(object):
__slots__ = 'foo', 'bar'
class Bar(object):
__slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
使用空
__slots__
在 parent 中似乎提供了最大的灵活性,
允许 child 选择阻止或允许 (通过添加
'__dict__'
来获得动态分配,请参见上面的部分)
创建一个 __dict__
:
class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'
您不必有插槽 - 所以如果您添加它们,然后删除它们,它应该不会引起任何问题。
__slots__
就子类的灵活性而言,这些父类似乎是最好的方法。
class AbstractBase:
__slots__ = ()
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
我们可以通过继承和声明预期的插槽来直接使用上面的内容:
class Foo(AbstractBase):
__slots__ = 'a', 'b'
但我们不在乎,那是微不足道的单继承,我们需要另一个我们也可以继承的类,也许有一个嘈杂的属性:
class AbstractBaseC:
__slots__ = ()
@property
def c(self):
print('getting c!')
return self._c
@c.setter
def c(self, arg):
print('setting c!')
self._c = arg
现在如果两个 base 都有非空槽,我们就不能做下面的事情。 (事实上,如果我们愿意,我们可以给
AbstractBase
非空槽 a 和 b,并将它们排除在下面的声明之外 - 将它们留在其中是错误的):
class Concretion(AbstractBase, AbstractBaseC):
__slots__ = 'a b _c'.split()
现在我们通过多重继承获得了两者的功能,并且仍然可以拒绝
__dict__
和
__weakref__
实例化:
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
避免插槽的其他情况:
__class__
除非插槽布局相同,否则与另一个没有它们的类(并且您不能添加它们)进行分配。 (我很想知道谁在做这件事以及为什么这样做。)__slots__
的其余部分中梳理出更多警告。
documentation (the 3.7 dev docs are the most current) ,我最近做出了重大贡献。
__slots__
”
"You would want to use
__slots__
if you are going to instantiate a lot (hundreds, thousands) of objects of the same class."
collections
模块,尚未实例化
__slots__
为他们宣布。
__dict__
或
__weakref__
创建,这些东西不能在父类中可用。
__slots__
在创建接口(interface)或 mixin 时有助于提高可重用性。
__slots__
不会破坏酸洗
TypeError
:
>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
这实际上是不正确的。此消息来自最旧的协议(protocol),这是默认的。您可以使用
-1
选择最新的协议(protocol)。争论。在 Python 2.7 中,这将是
2
(在 2.3 中引入),而在 3.6 中是
4
.
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
在 Python 2.7 中:
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
在 Python 3.6 中
>>> pickle.loads(pickle.dumps(f, 4))
<__main__.Foo object at 0x1129C770>
所以我会记住这一点,因为这是一个已解决的问题。
The proper use of
__slots__
is to save space in objects. Instead of having a dynamic dict that allows adding attributes to objects at anytime, there is a static structure which does not allow additions after creation. This saves the overhead of one dict for every object that uses slots
While this is sometimes a useful optimization, it would be completely unnecessary if the Python interpreter was dynamic enough so that it would only require the dict when there actually were additions to the object.
__dict__
当它被访问时,但是创建大量没有数据的对象是相当荒谬的。
__slots__
.以下不是避免插槽的真正原因(出于实际原因,请参阅我上面的其余答案。):
They change the behavior of the objects that have slots in a way that can be abused by control freaks and static typing weenies.
__slots__
相关的任何事情。 .
>>> class Foo(object): pass
>>> class Bar(object): __slots__ = ()
实例化一百万个:
>>> foos = [Foo() for f in xrange(1000000)]
>>> bars = [Bar() for b in xrange(1000000)]
用
guppy.hpy().heap()
检查:
>>> guppy.hpy().heap()
Partition of a set of 2028259 objects. Total size = 99763360 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 49 64000000 64 64000000 64 __main__.Foo
1 169 0 16281480 16 80281480 80 list
2 1000000 49 16000000 16 96281480 97 __main__.Bar
3 12284 1 987472 1 97268952 97 str
...
访问常规对象及其
__dict__
并再次检查:
>>> for f in foos:
... f.__dict__
>>> guppy.hpy().heap()
Partition of a set of 3028258 objects. Total size = 379763480 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 33 280000000 74 280000000 74 dict of __main__.Foo
1 1000000 33 64000000 17 344000000 91 __main__.Foo
2 169 0 16281480 4 360281480 95 list
3 1000000 33 16000000 4 376281480 99 __main__.Bar
4 12284 0 987472 0 377268952 99 str
...
这与 Python 的历史一致,来自
Unifying types and classes in Python 2.2
If you subclass a built-in type, extra space is automatically added to the instances to accomodate
__dict__
and__weakrefs__
. (The__dict__
is not initialized until you use it though, so you shouldn't worry about the space occupied by an empty dictionary for each instance you create.) If you don't need this extra space, you can add the phrase "__slots__ = []
" to your class.
关于python - __slots__ 的使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/472000/
首先让我说我了解槽和元类在 Python 中的工作方式。玩弄这两个,我遇到了一个有趣的问题。这是一个最小的例子: def decorator(cls): dct = dict(cls.__di
__slots__ 的目的是什么?在 Python 中——尤其是关于我什么时候想使用它,什么时候不想使用它? 最佳答案 In Python, what is the purpose of __slot
我在 Python 的输入系统和 __slots__ 之间有冲突。这是一个可重现的小例子。 from typing import TypeVar, Generic, Sequence T = Type
class Foo(object): __slots__ = ('a',) class Bar(Foo): @property def a(self): ret
我有一节课 __slots__ : class A: __slots__ = ('foo',) 如果我创建一个子类而不指定__slots__,子类将有一个__dict__: class B(A
我正在开发一个需要通过 __init__ 注入(inject)来为其赋予 __dict__ 属性的类,如下所示: class Torrent(Model): def __init__(self
我听说 __slots__ 通过避免字典查找使对象更快。我的困惑来自于 Python 是一种动态语言。在静态语言中,我们通过编译时优化将索引保存在我们运行的指令中,从而避免了对 a.test 的字典查
我需要用 None 初始化一个实例的所有插槽。如何获取派生类的所有插槽? 示例(不起作用): class A(object): __slots__ = "a" def __init__
所以,我正在阅读一些关于 Python 中元类的内容,以及如何使用 type()的三参数 alter-ego 用于动态创建类。但是,第三个参数通常是 dict。初始化要创建的类'__dict__变量。
考虑以下代码: from weakref import ref class Klass(object): # __slots__ = ['foo'] def __init__(self
通过学习《Python类变量和实例变量》一节,了解了如何动态的为单个实例对象添加属性,甚至如果必要的话,还可以为所有的类实例对象统一添加属性(通过给类添加属性)。 那么,Python 是否也允许动态地
早上好 我在尝试在我的一门类(class)中使用 __ 插槽 __ 时遇到问题。我想用它来优化 Classifier 对象的多个实例的创建。我正在使用 Python 3.7.1。 这行得通 class
当我运行这个(2.7.3)时,我得到这个输出: 'Slotted1' object has no attribute 'c' attribute c added to 我不明白 Slotted1 和
最近在学习Python,但是我有一个关于__slots__的问题。我认为是为了限制Class中的参数,同时也限制Class中的方法? 例如: from types import MethodType
最近给 friend 讲解了__slots__的用法。我想向他展示结果并使用了以下代码: import sys class Foo: __slots__ = 'a', 'b' def
我一直在玩弄 __slots__ 并稍微搜索一下,但我仍然对某些细节感到困惑: 我知道 __slots__ 生成某种描述符: >>> class C: ... __slots__ = ('x'
在类中创建结果对象时,是否可以在本例中使用__slots__?我想我可以通过将 '__slots__' 传递到 type 的第三个参数的字典中来让它工作: class GeocodeResult(ob
我读入了Usage of __slots__?在 Python 中使用 __slots__ 实际上可以节省时间。但是,当我尝试使用 datetime 查找所花费的时间时,结果却相反。 import d
我正在使用 Python 3.7 和 Django。我正在阅读 __slots__ .显然,__slots__ 可用于通过提前列出所有对象属性来优化大量此类对象的内存分配。 class MyClass
我正在编写一个配置卷的存储自动化模块。我没有传递在存储 Controller 上实际创建卷所需的六个或更多参数,而是使用 __slots__ 创建了一个参数类,它被传递到 create 方法中,如下所
我是一名优秀的程序员,十分优秀!