<blockquote>
<h2>What are named tuples?</h2>
</blockquote>
<p>命名元组是元组。</p>
<p>它能做任何事情。</p>
<p>但不仅仅是一个元组。</p>
<p>它是一个元组的特定子类,是根据您的规范以编程方式创建的,具有命名字段和固定长度。</p>
<p>例如,这将创建tuple的一个子类,除了具有固定长度(在本例中为3)之外,它还可以在使用tuple的任何地方使用,而不会中断。这被称为Liskov可替代性。</p>
<p><em><a href="https://docs.python.org/3/library/typing.html#typing.NamedTuple" rel="noreferrer">New in Python 3.6</a></em>,我们可以使用带有<code>typing.NamedTuple</code>的类定义来创建namedtuple:</p>
<pre class="lang-py prettyprint-override"><code>from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
</code></pre>
<p>上面的与下面的相同,只是上面另外有类型注释和docstring。以下是Python2+中提供的:</p>
<pre><code>>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)
</code></pre>
<p>这将实例化它:</p>
<pre><code>>>> ant = ANamedTuple(1, 'bar', [])
</code></pre>
<p>我们可以检查它并使用它的属性:</p>
<pre><code>>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']
</code></pre>
<h2>更深入的解释</h2>
<p>要理解命名元组,首先需要知道元组是什么。元组本质上是一个不可变(不能在内存中就地更改)列表。</p>
<p>下面是使用常规元组的方法:</p>
<pre><code>>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'
</code></pre>
<p>可以使用iterable解包来展开元组:</p>
<pre><code>>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
</code></pre>
<p>命名元组是元组,允许通过名称而不仅仅是索引来访问它们的元素!</p>
<p>你做了一个这样的名字:</p>
<pre><code>>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])
</code></pre>
<p>您还可以使用一个名称由空格分隔的字符串,这是对API的一种更可读的使用:</p>
<pre><code>>>> Student = namedtuple('Student', 'first last grade')
</code></pre>
<blockquote>
<h2>How to use them?</h2>
</blockquote>
<p>您可以执行元组可以执行的所有操作(参见上文)以及以下操作:</p>
<pre><code>>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')
</code></pre>
<p>一位评论者问道:</p>
<blockquote>
<p>In a large script or programme, where does one usually define a named tuple?</p>
</blockquote>
<p>用<code>namedtuple</code>创建的类型基本上是可以用简单的速记法创建的类。像上课一样对待他们。在模块级定义它们,以便pickle和其他用户可以找到它们。</p>
<p>在全局模块级别上的工作示例:</p>
<pre><code>>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')
</code></pre>
<p>这表明查找定义失败:</p>
<pre><code>>>> def foo():
... LocalNT = namedtuple('LocalNT', 'foo bar')
... return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed
</code></pre>
<blockquote>
<h2>Why/when should I use named tuples instead of normal tuples?</h2>
</blockquote>
<p>在改进代码以使元组元素的语义在代码中表达时使用它们。</p>
<p>如果要使用具有不变数据属性且没有功能的对象,则可以使用它们而不是对象。</p>
<p>您还可以<a href="https://docs.python.org/2/library/collections.html#namedtuple-factory-function-for-tuples-with-named-fields" rel="noreferrer">subclass them to add functionality, for example</a>:</p>
<pre><code>class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
</code></pre>
<blockquote>
<h2>Why/when should I use normal tuples instead of named tuples?</h2>
</blockquote>
<p>从使用命名元组切换到元组可能是一种回归。前期设计决策的核心是,当使用元组时,所涉及的额外代码的成本是否值得提高可读性。</p>
<p>命名元组与元组之间没有使用额外的内存。</p>
<blockquote>
<h2>Is there any kind of "named list" (a mutable version of the named tuple)?</h2>
</blockquote>
<p>您要寻找的是一个时隙对象,它实现了静态大小的列表的所有功能,或者是一个工作方式类似于命名元组的子类列表(它以某种方式阻止了列表的大小变化)</p>
<p>一个现在已经扩大的,甚至可能是Liskov可替代的,第一个例子:</p>
<pre><code>from collections import Sequence
class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""
__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
</code></pre>
<p>使用时,只需子类化并定义<code>__slots__</code>:</p>
<pre><code>class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize
>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A
</code></pre>