是否有理由更喜欢__slots__的列表或元组?

时间:2014-01-03 23:09:35

标签: python

您可以使用list或tuple(或者任何可迭代的?)在新式python类中定义__slots__。创建实例后类型仍然存在。

鉴于元组总是比列表更有效并且是不可变的,你有什么理由不想使用__slots__的元组吗?

>>> class foo(object):
...   __slots__ = ('a',)
... 
>>> class foo2(object):
...   __slots__ = ['a']
... 
>>> foo().__slots__
('a',)
>>> foo2().__slots__
['a']

2 个答案:

答案 0 :(得分:4)

首先,元组并不比列表更有效;它们都支持来自C API代码的完全相同的快速迭代机制,并使用相同的代码进行索引和从Python迭代。

更重要的是,__slots__机制实际上并不使用__slots__成员,除非在构建期间。这可能不是the documentation清楚解释的,但是如果你仔细阅读了所有的要点,那么信息就在那里。

实际上, 是真的。否则,这不起作用:

class Foo(object):
    __slots__ = (x for x in ['a', 'b', 'c'] if x != 'b')

......更糟糕的是,这会:

slots = ['a', 'b', 'c']
class Foo(object):
    __slots__ = slots
foo = Foo()
slots.append('d')
foo.d = 4

进一步证明:

>>> a = ['a', 'b']
>>> class Foo(object):
...     __slots__ = a
>>> del Foo.__slots__
>>> foo = Foo()
>>> foo.d = 3
AttributeError: 'Foo' object has no attribute 'd'
>>> foo.__dict__
AttributeError: 'Foo' object has no attribute '__dict__'
>>> foo.__slots__
AttributeError: 'Foo' object has no attribute '__slots__'

因此,__slots__中的Foo成员实际上仅用于文档和内省目的。这意味着没有性能问题或行为问题,只是风格问题。

答案 1 :(得分:0)

根据Python docs ..

  

可以为此类变量分配字符串,可迭代或序列   具有实例使用的变量名称的字符串。

因此,您可以使用任何可迭代来定义它。您使用哪一个取决于您,但就“优先”而言,我会使用一个列表。

首先,让我们看一下如果性能不是问题会是首选的选择,这意味着它将是所有Python代码中的same decision you would make between list and tuples。我会说一个列表,原因是因为元组设计具有语义结构:它应该在语义上表示将元素存储为第一个项而不是第二个项的东西。例如,如果将(X,Y)坐标元组(X)的第一个值存储为第二个项,则只需完全更改结构的语义值。如果重新排列__slots__列表中属性的名称,则表示您没有在语义上更改任何内容。因此,在这种情况下,您应该使用列表。

现在,关于表现。首先,这可能是过早的优化。我不知道列表和元组之间的性能差异,但我想不管怎么说都没有。但即使假设存在,如果多次访问__slots__变量,它真的只会发挥作用。

我实际上没有查看访问__slots__时的代码,但我运行了以下测试..

print('Defining slotter..')
class Slotter(object):
    def __iter__(self):
        print('Looking for slots')
        yield 'A'
        yield 'B'
        yield 'C'

print('Defining Mine..')
class Mine(object):
    __slots__ = Slotter()

print('Creating first mine...')
m1 = Mine()
m1.A = 1
m1.B = 2

print('Creating second mine...')
m2 = Mine()
m2.A = 1
m2.C = 2

基本上,我使用自定义类,以便我可以确切地看到slot变量实际迭代的时间。当定义类时,你会看到它完成了一次。

Defining slotter..
Defining Mine..
Looking for slots
Creating first mine...
Creating second mine...

除非在__slots__变量再次迭代的情况下遗漏了我的情况,否则我认为性能差异在最坏情况下可以忽略不计。