在Python中__slots__的目的是什么——特别是当我想要使用它时,什么时候不使用它?
当前回答
Another somewhat obscure use of __slots__ is to add attributes to an object proxy from the ProxyTypes package, formerly part of the PEAK project. Its ObjectWrapper allows you to proxy another object, but intercept all interactions with the proxied object. It is not very commonly used (and no Python 3 support), but we have used it to implement a thread-safe blocking wrapper around an async implementation based on tornado that bounces all access to the proxied object through the ioloop, using thread-safe concurrent.Future objects to synchronise and return results.
默认情况下,对代理对象的任何属性访问都将为您提供代理对象的结果。如果你需要在代理对象上添加一个属性,可以使用__slots__。
from peak.util.proxies import ObjectWrapper
class Original(object):
def __init__(self):
self.name = 'The Original'
class ProxyOriginal(ObjectWrapper):
__slots__ = ['proxy_name']
def __init__(self, subject, proxy_name):
# proxy_info attributed added directly to the
# Original instance, not the ProxyOriginal instance
self.proxy_info = 'You are proxied by {}'.format(proxy_name)
# proxy_name added to ProxyOriginal instance, since it is
# defined in __slots__
self.proxy_name = proxy_name
super(ProxyOriginal, self).__init__(subject)
if __name__ == "__main__":
original = Original()
proxy = ProxyOriginal(original, 'Proxy Overlord')
# Both statements print "The Original"
print "original.name: ", original.name
print "proxy.name: ", proxy.name
# Both statements below print
# "You are proxied by Proxy Overlord", since the ProxyOriginal
# __init__ sets it to the original object
print "original.proxy_info: ", original.proxy_info
print "proxy.proxy_info: ", proxy.proxy_info
# prints "Proxy Overlord"
print "proxy.proxy_name: ", proxy.proxy_name
# Raises AttributeError since proxy_name is only set on
# the proxy object
print "original.proxy_name: ", proxy.proxy_name
其他回答
除了在这里的其他答案中描述的无数优点-内存意识的紧凑实例,比更易变的__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
从Python 3.9开始,字典可用于通过__slots__向属性添加描述。没有描述的属性可以使用None,即使给出了描述,私有变量也不会出现。
class Person:
__slots__ = {
"birthday":
"A datetime.date object representing the person's birthday.",
"name":
"The first and last name.",
"public_variable":
None,
"_private_variable":
"Description",
}
help(Person)
"""
Help on class Person in module __main__:
class Person(builtins.object)
| Data descriptors defined here:
|
| birthday
| A datetime.date object representing the person's birthday.
|
| name
| The first and last name.
|
| public_variable
"""
如果你要实例化很多(成百上千)同一个类的对象,你会想要使用__slots__。__slots__仅作为内存优化工具存在。
强烈建议使用__slots__来约束属性创建。
使用__slots__ pickle对象将无法使用默认的(最古老的)pickle协议;有必要指定一个更高的版本。
python的其他一些自省特性也可能受到不利影响。
除了其他答案,这里还有一个使用__slots__的例子:
>>> class Test(object): #Must be new-style class!
... __slots__ = ['x', 'y']
...
>>> pt = Test()
>>> dir(pt)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
>>> pt.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> pt.x = 1
>>> pt.x
1
>>> pt.z = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'z'
>>> pt.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__dict__'
>>> pt.__slots__
['x', 'y']
因此,要实现__slots__,它只需要额外的一行(并使您的类成为一个新样式的类,如果它还不是的话)。通过这种方式,您可以将这些类的内存占用减少5倍,代价是必须编写自定义pickle代码(如果需要的话)。
类实例的属性有3个属性:实例、属性名和属性值。
在常规属性访问中,实例充当字典,属性名充当字典查找值中的键。
实例(属性)——>值
在__slots__访问中,属性的名称充当字典,实例充当字典查找值中的键。
属性(实例)——>值
在flyweight模式中,属性的名称充当字典,值充当查找实例的字典中的键。
属性(value)——>实例
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用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中获得所有直接子目录