什么是命名元组,我如何使用它们? 什么时候我应该使用命名元组而不是正常的元组,反之亦然? 也有“名单”吗?(即可变命名元组)
关于最后一个问题,请参见Python中是否存在可变命名元组。
什么是命名元组,我如何使用它们? 什么时候我应该使用命名元组而不是正常的元组,反之亦然? 也有“名单”吗?(即可变命名元组)
关于最后一个问题,请参见Python中是否存在可变命名元组。
当前回答
我认为添加使用类型提示的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
其他回答
在Python里面有一个很好的使用容器叫做命名元组,它可以用来创建一个类的定义,并具有原始元组的所有功能。
使用命名的tuple将直接应用到默认的类模板来生成一个简单的类,这种方法允许大量的代码来提高可读性,并且在定义类时也非常方便。
命名元组允许与这样检查版本的代码向后兼容
>>> sys.version_info[0:2]
(3, 1)
同时通过使用此语法允许未来的代码更加显式
>>> sys.version_info.major
3
>>> sys.version_info.minor
1
什么是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)
什么是命名元组?
命名元组是一个元组。
它做了元组能做的一切。
但它不仅仅是一个元组。
它是元组的特定子类,通过编程方式根据您的规范创建,具有命名字段和固定长度。
例如,它创建了一个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可以直观地视为定义类的快捷方式。
请参阅定义类的繁琐而传统的方法。
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'