什么是命名元组,我如何使用它们? 什么时候我应该使用命名元组而不是正常的元组,反之亦然? 也有“名单”吗?(即可变命名元组)


关于最后一个问题,请参见Python中是否存在可变命名元组。


当前回答

另一种使用命名元组的方法(一种新方法)是使用NamedTuple,来自typing package:在NamedTuple中输入提示

让我们用这篇文章中最上面的答案来看看如何使用它。

在使用命名元组之前,代码是这样的:

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt

line_length = sqrt((pt1[0] - pt2[0])**2 + (pt1[1] - pt2[1])**2)
print(line_length)

现在我们使用命名元组

from typing import NamedTuple

继承NamedTuple类并在新类中定义变量名。Test是类的名称。

class test(NamedTuple):
    x: float
    y: float

从类中创建实例并为它们赋值

pt1 = test(1.0, 5.0)   # x is 1.0, and y is 5.0. The order matters
pt2 = test(2.5, 1.5)

使用实例中的变量进行计算

line_length = sqrt((pt1.x - pt2.x)**2 + (pt1.y - pt2.y)**2)
print(line_length)

其他回答

namedtuple

是清理代码并使其更具可读性的最简单方法之一。它自我记录元组中发生的事情。Namedtuples实例的内存效率与常规元组一样高,因为它们没有每个实例的字典,这使得它们比字典更快。

from collections import namedtuple

Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])

 p = Color(170, 0.1, 0.6)
 if p.saturation >= 0.5:
     print "Whew, that is bright!"
 if p.luminosity >= 0.5:
     print "Wow, that is light"

如果不给元组中的每个元素命名,它就像这样:

p = (170, 0.1, 0.6)
if p[1] >= 0.5:
    print "Whew, that is bright!"
if p[2]>= 0.5:
   print "Wow, that is light"

要理解第一个例子中发生的事情要困难得多。使用namedtuple,每个字段都有一个名称。并且通过名称而不是位置或索引访问它。不叫p[1],叫p饱和。这样更容易理解。看起来更干净。

创建namedtuple的实例比创建字典容易。

# dictionary
>>>p = dict(hue = 170, saturation = 0.1, luminosity = 0.6)
>>>p['hue']
170

#nametuple
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p.hue
170

什么时候可以使用namedtuple

正如刚才所述,namedtuple使我们更容易理解元组 更容易。因此,如果需要引用元组中的项,则 将它们创建为命名元组是有意义的。 除了比字典更轻量级之外,命名元组也 不像字典那样有顺序。 与上面的示例一样,创建的实例更为简单 命名元组而不是字典。并引用已命名的项目 Tuple看起来比字典更简洁。P.hue而不是 p(“色相”)。

的语法

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])

namedtuple is in the collections library. typename: This is the name of the new tuple subclass. field_names: A sequence of names for each field. It can be a sequence as in a list ['x', 'y', 'z'] or string x y z (without commas, just whitespace) or x, y, z. rename: If rename is True, invalid fieldnames are automatically replaced with positional names. For example, ['abc', 'def', 'ghi','abc'] is converted to ['abc', '_1', 'ghi', '_3'], eliminating the keyword 'def' (since that is a reserved word for defining functions) and the duplicate fieldname 'abc'. verbose: If verbose is True, the class definition is printed just before being built.

如果你选择的话,你仍然可以通过命名元组的位置来访问它们。P [1] == P饱和。它仍然像常规元组一样解包。

方法

支持所有常规元组方法。例如:min(), max(), len(), in, not in,拼接(+),索引,切片等。namedtuple还有一些额外的。注意:这些都以下划线开头。_replace, _make, _asdict。

_replace 返回命名元组的新实例,用新值替换指定字段。

的语法

somenamedtuple._replace(kwargs)

例子

>>>from collections import namedtuple

>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)

>>>p._replace(hue=87)
Color(87, 0.1, 0.6)

>>>p._replace(hue=87, saturation=0.2)
Color(87, 0.2, 0.6)

注意:字段名不加引号;它们是关键字。 记住:元组是不可变的——即使它们是命名元组并且有_replace方法。_replace生成一个新实例;它不修改原始值或替换旧值。当然,您可以将新结果保存到变量中。P = P ._replace(hue=169)

_make

从现有序列或可迭代对象中创建一个新实例。

的语法

somenamedtuple._make(iterable)

例子

 >>>data = (170, 0.1, 0.6)
 >>>Color._make(data)
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make([170, 0.1, 0.6])  #the list is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make((170, 0.1, 0.6))  #the tuple is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make(170, 0.1, 0.6) 
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 15, in _make
TypeError: 'float' object is not callable

上一个怎么样了?括号内的项应该是可迭代对象。因此,括号内的列表或元组可以工作,但没有作为可迭代对象封装的值序列将返回错误。

_asdict

返回一个新的OrderedDict,它将字段名映射到相应的值。

的语法

somenamedtuple._asdict()

例子

 >>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])

参考:https://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

还有一个命名列表,它类似于命名元组,但是是可变的 https://pypi.python.org/pypi/namedlist

什么是namedtuple ?

顾名思义,namedtuple是带有name的元组。在标准元组中,我们使用索引访问元素,而namedtuple允许用户为元素定义名称。这是非常方便的,特别是处理csv(逗号分隔值)文件和处理复杂的大型数据集,其中代码因使用索引而变得混乱(不是那么python化)。

如何使用它们?

>>>from collections import namedtuple
>>>saleRecord = namedtuple('saleRecord','shopId saleDate salesAmout totalCustomers')
>>>
>>>
>>>#Assign values to a named tuple 
>>>shop11=saleRecord(11,'2015-01-01',2300,150) 
>>>shop12=saleRecord(shopId=22,saleDate="2015-01-01",saleAmout=1512,totalCustomers=125)

阅读

>>>#Reading as a namedtuple
>>>print("Shop Id =",shop12.shopId)
12
>>>print("Sale Date=",shop12.saleDate)
2015-01-01
>>>print("Sales Amount =",shop12.salesAmount)
1512
>>>print("Total Customers =",shop12.totalCustomers)
125

CSV处理中的有趣场景:

from csv import reader
from collections import namedtuple

saleRecord = namedtuple('saleRecord','shopId saleDate totalSales totalCustomers')
fileHandle = open("salesRecord.csv","r")
csvFieldsList=csv.reader(fileHandle)
for fieldsList in csvFieldsList:
    shopRec = saleRecord._make(fieldsList)
    overAllSales += shopRec.totalSales;

print("Total Sales of The Retail Chain =",overAllSales)

我认为添加使用类型提示的NamedTuples的信息是值得的:

# dependencies
from typing import NamedTuple, Optional

# definition
class MyNamedTuple(NamedTuple):
    an_attribute: str
    my_attribute: Optional[str] = None
    next_attribute: int = 1

# instantiation
my_named_tuple = MyNamedTuple("abc", "def")
# or more explicitly:
other_tuple = MyNamedTuple(an_attribute="abc", my_attribute="def")

# access
assert "abc" == my_named_tuple.an_attribute
assert 1 == other_tuple.next_attribute

什么是命名元组?

命名元组是一个元组。

它做了元组能做的一切。

但它不仅仅是一个元组。

它是元组的特定子类,通过编程方式根据您的规范创建,具有命名字段和固定长度。

例如,它创建了一个tuple的子类,除了长度固定(在本例中为3)之外,它可以在任何使用tuple的地方使用而不中断。这就是所谓的利斯科夫可替代性。

在Python 3.6中,我们可以使用带有类型的类定义。创建一个NamedTuple:

from typing import NamedTuple

class ANamedTuple(NamedTuple):
    """a docstring"""
    foo: int
    bar: str
    baz: list

上面的内容与collections.namedtuple相同,只是上面的内容额外增加了类型注释和文档字符串。以下代码在Python 2+中可用:

>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)

这将实例化它:

>>> ant = ANamedTuple(1, 'bar', [])

我们可以检查它并使用它的属性:

>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']

更深层次的解释

要理解命名元组,首先需要知道什么是元组。元组本质上是一个不可变(不能在内存中原地更改)列表。

下面是常规元组的使用方法:

>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'

你可以用iterable unpacking展开元组:

>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'

命名元组是允许通过名称访问其元素的元组,而不仅仅是索引!

你像这样创建一个命名元组:

>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])

你也可以使用一个字符串,名称之间用空格分隔,这是一种更易于阅读的API用法:

>>> Student = namedtuple('Student', 'first last grade')

如何使用它们?

你可以做元组能做的所有事情(见上面),也可以做以下事情:

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

一位评论者问道:

在大型脚本或程序中,通常在哪里定义命名元组?

使用namedtuple创建的类型基本上是可以用简单的简写创建的类。像对待班级一样对待他们。在模块级别上定义它们,以便pickle和其他用户可以找到它们。

在全局模块级别上的工作示例:

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

这说明了查找定义的失败:

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

为什么/什么时候我应该使用命名元组而不是普通元组?

在改进代码时使用它们,以便在代码中表达元组元素的语义。

如果要使用具有不变数据属性且没有功能的对象,则可以使用它们而不是对象。

你也可以子类化它们来添加功能,例如:

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)

为什么/什么时候我应该使用普通元组而不是命名元组?

从使用命名元组切换到使用元组可能是一种倒退。前期设计决策的核心是,当使用元组时,所涉及的额外代码成本是否值得改进的可读性。

与元组相比,命名元组不使用额外的内存。

是否存在任何类型的“命名列表”(命名元组的可变版本)?

您要寻找的要么是实现静态大小列表的所有功能的插槽对象,要么是工作方式类似于命名元组的子类列表(并且以某种方式阻止列表大小的变化)。

现在扩展的,甚至可以用利斯科夫代替的,第一个例子:

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

要使用,只需子类化并定义__slots__:

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

命名元组允许与这样检查版本的代码向后兼容

>>> sys.version_info[0:2]
(3, 1)

同时通过使用此语法允许未来的代码更加显式

>>> sys.version_info.major
3
>>> sys.version_info.minor
1