Python 是否有理由选择使用列表或元组作为插槽?

Python 是否有理由选择使用列表或元组作为插槽?,python,Python,您可以使用列表或元组(或者任何iterable?)在新型python类中定义\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。创建实例后,该类型将持续存在 考虑到元组总是比列表更有效,并且是不可变的,有什么理由不想在\uuuuuuu插槽中使用元组吗 >>> class foo(object): ... __slots__ = ('a',)

您可以使用列表或元组(或者任何iterable?)在新型python类中定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
。创建实例后,该类型将持续存在

考虑到元组总是比列表更有效,并且是不可变的,有什么理由不想在
\uuuuuuu插槽中使用元组吗

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

可以为该类变量分配字符串、iterable或 实例使用的具有变量名的字符串

因此,您可以使用任何iterable来定义它。你用哪一个取决于你,但就“更喜欢”哪一个而言,我会用一个列表

首先,让我们看看如果性能不是一个问题,那么什么是首选,这意味着它将是所有Python代码中的首选。我会说是一个列表,原因是元组被设计成具有语义结构:它应该在语义上意味着将元素存储为第一项而不是第二项。例如,如果将(X,Y)坐标元组(X)的第一个值存储为第二项,则只需完全更改结构的语义值。如果重新排列
\uuu slots\uuu
列表中属性的名称,则在语义上没有任何更改。因此,在这种情况下,应该使用列表

现在,关于性能。首先,这可能是过早的优化。我不知道列表和元组之间的性能差异,但我猜无论如何都没有。但是,即使假设存在,它也只有在多次访问
\uuuu slots\uuu
变量时才会真正起作用

实际上,我还没有查看何时访问
\uuuuuu插槽\uuuuuu
的代码,但我运行了以下测试

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
基本上,我使用了一个自定义类,这样我就可以准确地看到slots变量何时实际被迭代。您将看到,在定义类时,它只执行一次

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

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

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

更重要的是,
\uuuuuu slots\uuuu
机制实际上不使用
\uuuuuuu slots\uuuu
成员,除非在构造期间。这可能不是很清楚的解释,但如果你仔细阅读所有要点,信息就在那里

事实上,这必须是真的。否则,这将不起作用:

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__'

因此,
Foo
中的
\uuuuuuuu slots\uuuuuuu
成员实际上只是出于文档和内省的目的。这意味着没有性能问题,也没有行为问题,只是风格问题。

作为一个网站说明,我怀疑你,像may people一样,假设
\uuuuuuu slots\uuuuuuu
在某种程度上给了你更像是一个隐藏的C结构的东西,它更快、更紧凑,这甚至可能是你首先使用它的原因。如果是这样的话:插槽使用描述符,因此访问
foo.a
基本上是
foo.a.get(foo)
而不是通常的
foo.\uu dict\uuu['a']
,后者通常比较慢而不是更快。优点是为每个实例保存一个
dict
对象,如果您有很多实例和很少的属性(正如文档明确指出的),而不是效率。仅供参考,在Python3.3中,有一个新的
dict
实现(请参阅),它似乎使
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。更多信息请参见。@aquavitae:我认为答案是错误的<代码>\uuuuuuuuuuuuuuuu插槽\uuuuuuuuuu
对于值得使用的大多数用例来说仍然不是多余的。“首先,元组并不比列表更有效”--玩弄时间它反驳了这一点:你在用它们做什么,你在测试什么<代码>ll=[randrange(100)表示范围内(10000)];tt=tuple(ll)
后接
%timeit ll[-100]
%timeit tt[-100]
的tuple赢4次,输2次,但两种方式的速度都不会超过3.5%。
timeit.Timer(“对于x中的i:pass”,“x=(1,2,3,4,5,6)”
始终比
timeit.Timer(“对于x中的i:pass”,“x=[1,2,3,4,5,6]”
在我的系统上@ʞɔıu:在我的linux机器上,用你的精确代码,我的3.2.3速度快了2.8%,2.7.3速度慢了3.2%,2.7.6速度慢了0.6%;在我的Mac电脑上,3.4b1速度慢了1.9%,3.3.2速度慢了1.3%,2.7.5速度快了2.9%(所有64位CPython)但是,在同一个微小序列上重复循环的性能在任何实际代码中都不太可能有意义(例如,在现实生活中,99.99999%的运行都不会将整个序列放在缓存中)@abarnert我通过使用
%timeit
获得了与他第一次评论相同的结果。有没有办法在不再次访问
\uuuuuuuuu slots\uuuuu
的情况下获取带有插槽的对象的属性列表?re:元组被设计为具有语义结构:它应该在语义上表示将元素存储为第一项而不是第二项的内容"--我认为这是为什么要使用元组而不是整个集合的一部分原因。另一个原因是可变性,而且绝对没有理由认为
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>是可变性的。在我看来这完全是假的。
列表和
元组都是序列,两者的顺序都是重要的。如果你真的想要一个语义上暗示顺序无关紧要的容器,我想你可以使用
set
,因为
\uuuuuuuuuuuuuuuuuuu>不需要重复。即使你接受同质与异质的区别(这是可变大小与固定大小的副作用,但是让我们来看看