在Python中__slots__的目的是什么——特别是当我想要使用它时,什么时候不使用它?
当前回答
插槽对于库调用非常有用,可以在进行函数调用时消除“命名方法分派”。SWIG文档中提到了这一点。对于想要减少常用调用函数的函数开销的高性能库来说,使用插槽要快得多。
这可能和OPs问题没有直接关系。它更多地与构建扩展有关,而不是与在对象上使用插槽语法有关。但它确实有助于完善插槽的使用情况以及它们背后的一些原因。
其他回答
类实例的属性有3个属性:实例、属性名和属性值。
在常规属性访问中,实例充当字典,属性名充当字典查找值中的键。
实例(属性)——>值
在__slots__访问中,属性的名称充当字典,实例充当字典查找值中的键。
属性(实例)——>值
在flyweight模式中,属性的名称充当字典,值充当查找实例的字典中的键。
属性(value)——>实例
如果你要实例化很多(成百上千)同一个类的对象,你会想要使用__slots__。__slots__仅作为内存优化工具存在。
强烈建议使用__slots__来约束属性创建。
使用__slots__ pickle对象将无法使用默认的(最古老的)pickle协议;有必要指定一个更高的版本。
python的其他一些自省特性也可能受到不利影响。
除了其他答案,__slots__还通过将属性限制在预定义的列表中增加了一点排版安全性。这一直是JavaScript的一个问题,它还允许您向现有对象添加新属性,无论您是否有意。
下面是一个普通的unslot对象,它什么都不做,但是允许你添加属性:
class Unslotted:
pass
test = Unslotted()
test.name = 'Fred'
test.Name = 'Wilma'
由于Python是区分大小写的,所以拼写相同但大小写不同的两个属性是不同的。如果你怀疑其中一个是打字错误,那就太倒霉了。
使用插槽,你可以限制它:
class Slotted:
__slots__ = ('name')
pass
test = Slotted()
test.name = 'Fred' # OK
test.Name = 'Wilma' # Error
这一次,第二个属性(Name)是不允许的,因为它不在__slots__集合中。
我建议在可能的情况下使用__slots__更好,以保持对对象的更多控制。
除了在这里的其他答案中描述的无数优点-内存意识的紧凑实例,比更易变的__dict__承载实例更不容易出错等等-我发现使用__slots__提供了更清晰的类声明,因为类的实例变量显式地公开。
为了解决__slots__声明的继承问题,我使用了这个元类:
import abc
class Slotted(abc.ABCMeta):
""" A metaclass that ensures its classes, and all subclasses,
will be slotted types.
"""
def __new__(metacls, name, bases, attributes, **kwargs):
""" Override for `abc.ABCMeta.__new__(…)` setting up a
derived slotted class.
"""
if '__slots__' not in attributes:
attributes['__slots__'] = tuple()
return super(Slotted, metacls).__new__(metacls, name, # type: ignore
bases,
attributes,
**kwargs)
…如果在继承塔中声明为基类的元类,则确保从该基类派生的所有内容都将正确继承__slots__属性,即使中间类没有声明任何属性。像这样:
# note no __slots__ declaration necessary with the metaclass:
class Base(metaclass=Slotted):
pass
# class is properly slotted, no __dict__:
class Derived(Base):
__slots__ = 'slot', 'another_slot'
# class is also properly slotted:
class FurtherDerived(Derived):
pass
__slot__属性的一个非常简单的例子。
问题:没有__slots__
如果我的类中没有__slot__属性,我可以向对象添加新属性。
class Test:
pass
obj1=Test()
obj2=Test()
print(obj1.__dict__) #--> {}
obj1.x=12
print(obj1.__dict__) # --> {'x': 12}
obj1.y=20
print(obj1.__dict__) # --> {'x': 12, 'y': 20}
obj2.x=99
print(obj2.__dict__) # --> {'x': 99}
如果你看上面的例子,你可以看到obj1和obj2有它们自己的x和y属性,python还为每个对象(obj1和obj2)创建了一个dict属性。
假设我的类Test有数千个这样的对象?在我的代码中,为每个对象创建一个额外的属性字典将导致大量的开销(内存,计算能力等)。
解决方案:使用__slots__
现在在下面的例子中,我的类Test包含__slots__属性。现在我不能添加新的属性到我的对象(属性x除外)和python不再创建dict属性。这消除了每个对象的开销,如果您有许多对象,这将变得非常重要。
class Test:
__slots__=("x")
obj1=Test()
obj2=Test()
obj1.x=12
print(obj1.x) # --> 12
obj2.x=99
print(obj2.x) # --> 99
obj1.y=28
print(obj1.y) # --> AttributeError: 'Test' object has no attribute 'y'
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录