__slots__的用法?

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


#1楼

插槽对于库调用非常有用,以消除进行函数调用时的“命名方法分派”。 SWIG 文档中对此进行了提及。 对于想要减少使用插槽的通常称为函数的函数开销的高性能库,速度要快得多。

现在,这可能与OP问题没有直接关系。 它与构建扩展有关,而不是与在对象上使用slot语法有关。 但这确实有助于完整了解插槽的使用情况以及它们背后的一些原因。


#2楼

类实例的属性具有3个属性:实例,属性名称和属性值。

常规属性访问中 ,实例充当字典,而属性名称充当该字典中查找值的键。

instance(attribute)->值

__slots__ access中 ,属性名称充当字典,实例充当字典中查找值的键。

属性(实例)->值

flyweight模式中 ,属性的名称充当字典,而值充当该字典中查找实例的键。

attribute(value)->实例


#3楼

在Python中, __slots__的目的是什么?在什么情况下应避免这种情况?

TLDR:

特殊属性__slots__允许您显式说明您希望对象实例具有哪些实例属性,并具有预期的结果:

  1. 更快的属性访问。
  2. 节省内存空间

节省的空间来自

  1. 将值引用存储在插槽中而不是__dict__
  2. 如果父类拒绝__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不会反对(它应该会),否则问题可能不会显现出来,但是您的对象将比原先占用更多的空间。

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

最大的警告是多重继承-无法合并多个“具有非空插槽的父类”。

为适应此限制,请遵循最佳实践:排除其父母和您的新具体类将分别继承的一个或所有父类的所有抽象-给这些抽象空的位置(就像在父类中的抽象基类一样)标准库)。

有关示例,请参见下面有关多重继承的部分。

要求:

  • 要使在__slots__命名的属性实际上存储在插槽中而不是在__dict__存储,类必须从object继承。

  • 为了防止创建__dict__ ,您必须从object继承,并且继承中的所有类都必须声明__slots__并且它们都不能具有'__dict__'条目。

如果您想继续阅读,有很多细节。

为什么使用__slots__ :更快的属性访问。

Python的创建者Guido van Rossum ,他实际上创建了__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 3.5上的Python 3.5中,插槽式访问的速度几乎快了30%。

>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342

在Windows上的Python 2中,我测得的速度要快15%。

为什么使用__slots__ :节省内存

__slots__另一个目的是减少每个对象实例占用的内存空间。

我自己对文档的贡献清楚地说明了背后的原因

使用__dict__节省的空间可能很大。

SQLAlchemy将大量内存节省归因__slots__

为了验证这一点,请在Ubuntu Linux上使用Python 2.7的Anaconda发行版,并带有guppy.hpy (又称堆)和sys.getsizeof ,不声明__slots__且没有其他声明的类实例的大小为64字节。 这包括__dict__ 。 再次感谢Python的惰性求值,显然在引用__dict__之前,它并不存在,但是没有数据的类通常是无用的。 当被调用时, __dict__属性至少要最少280个字节。

相反,声明为() (无数据)的__slots__的类实例只有16个字节,并且插槽中有一项的总字节数为56个,插槽中有两项的64个字节。

对于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

因此,尽管Python 3中的指令较小,但我们看到__slots__可以很好地扩展实例以节省内存,这是您想使用__slots__主要原因。

仅出于我的注意事项的完整性,请注意,在类的名称空间中,每个插槽的一次性成本为Python 2中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值