是否有理由偏爱__slots__的列表或元组?

ʞɔı*_*ɔıu 5 python

您可以__slots__使用列表或元组(或任何可迭代的?)在新型python类中进行定义。创建实例后,该类型仍然存在。

鉴于元组总是比列表更有效率并且是不变的,是否有任何理由不希望使用元组__slots__呢?

>>> class foo(object):
...   __slots__ = ('a',)
... 
>>> class foo2(object):
...   __slots__ = ['a']
... 
>>> foo().__slots__
('a',)
>>> foo2().__slots__
['a']
Run Code Online (Sandbox Code Playgroud)

aba*_*ert 5

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

更重要的是,除了在构造过程中,该__slots__机制实际上并不使用该__slots__成员。文档可能没有对此进行清楚的解释,但是如果您仔细阅读了所有要点,则信息就足够了。

实际上,它必须是真实的。否则,这将行不通:

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

……更糟糕的是:

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

为了进一步证明:

>>> 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__'
Run Code Online (Sandbox Code Playgroud)

因此,该__slots__成员Foo实际上仅是出于文档和自省的目的。这意味着不存在性能问题或行为问题,仅是一种样式问题。

  • “首先,元组并不比列表更有效”——玩弄时间反驳了这一点 (2认同)
  • `timeit.Timer("for i in x: pass", "x = (1, 2, 3, 4, 5, 6)")` 始终比 `timeit.Timer("for i in x : pass", "x = [1, 2, 3, 4, 5, 6]")` 在我的系统上 (2认同)

Mar*_*eth 0

根据Python文档..

可以为此类变量分配一个字符串、可迭代对象或带有实例使用的变量名称的字符串序列。

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

首先,让我们看看如果性能不是问题的话,什么是首选,这意味着您在所有 Python 代码中在列表和元组之间做出的决定都是相同的。我会说一个列表,原因是因为元组被设计为具有语义结构:它在语义上应该意味着您将元素存储为第一项而不是第二项。例如,如果您将 (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
Run Code Online (Sandbox Code Playgroud)

基本上,我使用自定义类,以便我可以准确地看到插槽变量何时实际迭代。您将看到,当定义类时,它只完成一次。

Defining slotter..
Defining Mine..
Looking for slots
Creating first mine...
Creating second mine...
Run Code Online (Sandbox Code Playgroud)

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

  • 在我看来这完全是假的。`list` 和 `tuple` 都是序列,两者的顺序都很重要。如果您确实想要一个在语义上暗示顺序无关紧要的容器,我想您可以使用“set”,因为“__slots__”不需要重复项。即使您接受同构与异构的区别(IMO 是可变大小与固定大小的副作用,但我们不要去那里),它并不意味着“列表”意味着*无序*数据。 (2认同)