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


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


当前回答

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

其他回答

在Python里面有一个很好的使用容器叫做命名元组,它可以用来创建一个类的定义,并具有原始元组的所有功能。

使用命名的tuple将直接应用到默认的类模板来生成一个简单的类,这种方法允许大量的代码来提高可读性,并且在定义类时也非常方便。

什么是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)
  from collections import namedtuple 

他们子类化tuple,并添加一个层来为位置元素分配属性名

'namedtuple'是一个函数,它生成一个继承自“tuple”的新类,但也提供了“命名属性”来访问元组的元素。

生成命名元组类

"namedtuple"是一个类工厂。它需要一些东西来生成类

the class name A sequence of field names we want to assign, in the order of elements in the tuple. Field names can be any valid variable names except that they cannot start with an "underscore". The return value of the call to "namedtuple" will be a class. We need to assign that class to a variable name in our code so we can use it to construct instances. In general, we use the same name as the name of the class that was generated. # Coords is a class Coords = namedtuple('Coords', ['x', 'y']) Now we can create instances of Coords class: pt=Coords(10,20) There are many ways we can provide the list of field names to the namedtuple function. a list of strings namedtuple('Coords',['x','y']) a tuple of strings namedtuple('Coords',('x','y')) a single string with the field names separated by whitespace or commas namedtuple('Coords','x, y'])

实例化命名元组

创建了命名的tuple类之后,可以像实例化普通类一样实例化它们。事实上,生成类的__new__方法使用我们作为参数名提供的字段名。

Coords = namedtuple('Coords', ['x', 'y'])
coord=Coords(10,20) 

访问命名元组中的数据:

由于命名元组继承自元组,我们仍然可以像处理其他元组一样处理它们:通过索引、切片、迭代

Coords = namedtuple('Coords', ['x', 'y'])
coord=Coords(10,20)       isinstance(coord,tuple) --> True # namedtuple is subclass of tuple

x,y=coord  # Unpacking
x=coord[0] # by index
for e in coord:
    print(e)

现在,我们还可以使用字段名访问数据,就像使用类一样。 坐标。x——> 10 坐标。y——> 20 由于namedtuple是由继承自tuple的类生成的,我们可以这样写: 类Coord(元组): .... "coord"是一个元组,因此是不可变的

"rename"关键字参数为namedtuple

字段名不能以下划线开头

  Coords = namedtuple('Coords', ['x', '_y']) # does not work

namedtuple有一个只包含关键字的参数rename(默认为False),它将自动重命名任何无效的字段名。

Coords = namedtuple('Coords', ['x', '_y'], rename=True)

字段名称“x”不会改变,但“_y”将改变为_1。1是字段名的索引。

想象一下这样的场景:您需要更新应用程序,因此希望使用namedTuple存储应用程序的用户。您需要提取列名,但它们对于命名元组无效,并且会抛出异常。在本例中,使用rename=True。

将命名元组值提取到字典中

Coords = namedtuple('Coords', ['x', 'y'])
coord=Coords(10,20)
coord._asdict()
   {'x': 10, 'y': 20}

为什么使用namedtuple

如果你有这样的课程:

class Stock:
    def __init__(self, symbol, year, month, day, open, high, low, close):
        self.symbol = symbol
        self.year = year
        self.month = month
        self.day = day
        self.open = open 
        self.high = high
        self.low = low
        self.close = close

类方法vs元组方法

stock.symbol              stock[0]     
stock.open                stock[4]
stock.close               stock[7]
stock.high – stock.low     stock[5] – stock[6]

如您所见,元组方法是不可读的。集合中的namedtuple函数允许我们创建一个元组,每个字段或属性都有附加的名称。这可以方便地通过“名称”引用元组结构中的数据,而不仅仅依赖于位置。但请记住,元组是不可变的,所以如果你想要可变性,请坚持使用类

因为namedtuple是可迭代的,你可以使用iterable方法。例如,如果你有" coordinates "作为类实例,你不能查找最大坐标是多少,但是使用name -tuple,你可以。

我认为添加使用类型提示的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