什么是命名元组,我如何使用它们? 什么时候我应该使用命名元组而不是正常的元组,反之亦然? 也有“名单”吗?(即可变命名元组)
关于最后一个问题,请参见Python中是否存在可变命名元组。
什么是命名元组,我如何使用它们? 什么时候我应该使用命名元组而不是正常的元组,反之亦然? 也有“名单”吗?(即可变命名元组)
关于最后一个问题,请参见Python中是否存在可变命名元组。
当前回答
其他人都已经回答了,但我想我还有一些东西要补充。
Namedtuple可以直观地视为定义类的快捷方式。
请参阅定义类的繁琐而传统的方法。
class Duck:
def __init__(self, color, weight):
self.color = color
self.weight = weight
red_duck = Duck('red', '10')
In [50]: red_duck
Out[50]: <__main__.Duck at 0x1068e4e10>
In [51]: red_duck.color
Out[51]: 'red'
至于namedtuple
from collections import namedtuple
Duck = namedtuple('Duck', ['color', 'weight'])
red_duck = Duck('red', '10')
In [54]: red_duck
Out[54]: Duck(color='red', weight='10')
In [55]: red_duck.color
Out[55]: 'red'
其他回答
什么是命名元组?
命名元组是一个元组。
它做了元组能做的一切。
但它不仅仅是一个元组。
它是元组的特定子类,通过编程方式根据您的规范创建,具有命名字段和固定长度。
例如,它创建了一个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
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
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,你可以。
命名元组是一个很好的特性,它们是数据的完美容器。当你必须“存储”数据时,你会使用元组或字典,比如:
user = dict(name="John", age=20)
or:
user = ("John", 20)
字典方法是压倒性的,因为字典是可变的,比元组慢。另一方面,元组是不可变和轻量级的,但是对于数据字段中的大量条目缺乏可读性。
命名元组是这两种方法的完美折衷,它们具有很高的可读性、轻量级性和不可变性(而且它们是多态的!)
Namedtuple是一个用于创建元组类的工厂函数。通过这个类,我们还可以创建可按名称调用的元组。
import collections
#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"])
row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created
print row #Prints: Row(a=1, b=2, c=3)
print row.a #Prints: 1
print row[0] #Prints: 1
row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values
print row #Prints: Row(a=2, b=3, c=4)