在Python中__slots__的目的是什么——特别是当我想要使用它时,什么时候不使用它?
当前回答
本质上,你没有使用__slots__。
当你认为你可能需要__slots__时,你实际上想要使用轻量级或Flyweight设计模式。在这些情况下,您不再希望使用纯Python对象。相反,您需要一个Python类对象的包装器来包装数组、结构体或numpy数组。
class Flyweight(object):
def get(self, theData, index):
return theData[index]
def set(self, theData, index, value):
theData[index]= value
类包装器没有属性——它只提供作用于底层数据的方法。方法可以简化为类方法。实际上,它可以简化为仅对底层数据数组进行操作的函数。
其他回答
每个python对象都有一个__dict__属性,它是一个包含所有其他属性的字典。例如,当你输入self时。Attr python实际上正在执行self.__dict__[' Attr ']。你可以想象使用字典来存储属性需要一些额外的空间和时间来访问它。
然而,当你使用__slots__时,为该类创建的任何对象都不会有__dict__属性。相反,所有属性访问都直接通过指针完成。
所以如果你想要一个C风格的结构而不是一个完整的类,你可以使用__slots__来压缩对象的大小并减少属性访问时间。一个很好的例子是一个包含属性x和y的Point类。如果你要有很多点,你可以尝试使用__slots__来节省一些内存。
最初的问题是关于一般用例,而不仅仅是关于内存。 因此,这里应该提到的是,当实例化大量对象时,您也会获得更好的性能——有趣的是,当将大型文档解析为对象或从数据库中解析时。
下面是使用插槽和不使用插槽创建具有一百万个条目的对象树的比较。作为对树使用普通字典时的性能参考(OSX上的Py2.7.10):
********** RUN 1 **********
1.96036410332 <class 'css_tree_select.element.Element'>
3.02922606468 <class 'css_tree_select.element.ElementNoSlots'>
2.90828204155 dict
********** RUN 2 **********
1.77050495148 <class 'css_tree_select.element.Element'>
3.10655999184 <class 'css_tree_select.element.ElementNoSlots'>
2.84120798111 dict
********** RUN 3 **********
1.84069895744 <class 'css_tree_select.element.Element'>
3.21540498734 <class 'css_tree_select.element.ElementNoSlots'>
2.59615707397 dict
********** RUN 4 **********
1.75041103363 <class 'css_tree_select.element.Element'>
3.17366290092 <class 'css_tree_select.element.ElementNoSlots'>
2.70941114426 dict
测试类(标识,除了槽):
class Element(object):
__slots__ = ['_typ', 'id', 'parent', 'childs']
def __init__(self, typ, id, parent=None):
self._typ = typ
self.id = id
self.childs = []
if parent:
self.parent = parent
parent.childs.append(self)
class ElementNoSlots(object): (same, w/o slots)
Testcode,详细模式:
na, nb, nc = 100, 100, 100
for i in (1, 2, 3, 4):
print '*' * 10, 'RUN', i, '*' * 10
# tree with slot and no slot:
for cls in Element, ElementNoSlots:
t1 = time.time()
root = cls('root', 'root')
for i in xrange(na):
ela = cls(typ='a', id=i, parent=root)
for j in xrange(nb):
elb = cls(typ='b', id=(i, j), parent=ela)
for k in xrange(nc):
elc = cls(typ='c', id=(i, j, k), parent=elb)
to = time.time() - t1
print to, cls
del root
# ref: tree with dicts only:
t1 = time.time()
droot = {'childs': []}
for i in xrange(na):
ela = {'typ': 'a', id: i, 'childs': []}
droot['childs'].append(ela)
for j in xrange(nb):
elb = {'typ': 'b', id: (i, j), 'childs': []}
ela['childs'].append(elb)
for k in xrange(nc):
elc = {'typ': 'c', id: (i, j, k), 'childs': []}
elb['childs'].append(elc)
td = time.time() - t1
print td, 'dict'
del droot
插槽对于库调用非常有用,可以在进行函数调用时消除“命名方法分派”。SWIG文档中提到了这一点。对于想要减少常用调用函数的函数开销的高性能库来说,使用插槽要快得多。
这可能和OPs问题没有直接关系。它更多地与构建扩展有关,而不是与在对象上使用插槽语法有关。但它确实有助于完善插槽的使用情况以及它们背后的一些原因。
__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'
除了其他答案,这里还有一个使用__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代码(如果需要的话)。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用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中获得所有直接子目录