什么是Python中的“命名元组”?

阅读Python 3.1的变化 ,我发现了一些…意想不到的事情:

sys.version_info元组现在是一个命名的元组

我从来没有听说过命名元组,我认为元素可以通过数字(如元组和列表)或键(如在字典中)进行索引。 我从来没有想到他们可以被两种方式索引。

因此,我的问题是:

  • 什么是命名元组?
  • 如何使用它们?
  • 为什么/何时应该使用命名的元组而不是普通的元组?
  • 为什么/何时应该使用普通的元组而不是命名的元组?
  • 是否有任何一种“命名列表”(命名元组的可变版本)?

命名元组基本上是易于创建的轻量级对象类型。 命名的元组实例可以使用类似于对象的变量解引用或标准元组语法来引用。 它们可以类似于struct或其他常见记录类型使用,除了它们是不可变的。 他们是在Python 2.6和Python 3.0中添加的,尽管在Python 2.4中有一个实现的配方 。

例如,将一个点表示为一个元组(x, y)是很常见的。 这导致代码如下所示:

 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) 

使用一个命名的元组变得更可读:

 from collections import namedtuple Point = namedtuple('Point', 'x y') pt1 = Point(1.0, 5.0) pt2 = Point(2.5, 1.5) from math import sqrt line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2) 

然而,命名元组仍然向后兼容正常的元组,所以下面的方法仍然有效:

 Point = namedtuple('Point', 'x y') pt1 = Point(1.0, 5.0) pt2 = Point(2.5, 1.5) from math import sqrt # use index referencing line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2) # use tuple unpacking x1, y1 = pt1 

因此, 你应该在任何你认为使用对象符号的地方使用命名的元组而不是元组,这样会使得你的代码变得更加pythonic,并且更易读 。 我个人已经开始使用它们来表示非常简单的值类型,特别是当它们作为参数传递给函数时。 它使得函数更具可读性,而无需查看元组打包的上下​​文。

此外, 你也可以替换普通的不可变类,它们没有函数 ,只有字段。 你甚至可以使用你的命名元组类型作为基类:

 class Point(namedtuple('Point', 'x y')): [...] 

但是,与元组一样,命名元组中的属性是不可变的:

 >>> Point = namedtuple('Point', 'x y') >>> pt1 = Point(1.0, 5.0) >>> pt1.x = 2.0 AttributeError: can't set attribute 

如果您想要更改这些值,则需要另一种类型。 有一个方便的配方可变的记录类型 ,可以让你设置新的属性值。

 >>> from rcdtype import * >>> Point = recordtype('Point', 'x y') >>> pt1 = Point(1.0, 5.0) >>> pt1 = Point(1.0, 5.0) >>> pt1.x = 2.0 >>> print(pt1[0]) 2.0 

我不知道任何形式的“名单”,可以让你添加新的领域,但是。 你可能只想在这种情况下使用字典。 命名元组可以使用返回{'x': 1.0, 'y': 5.0} pt1._asdict()转换为字典{'x': 1.0, 'y': 5.0}并且可以使用所有常用的字典功能进行操作。

如前所述,您应该查看文档以获取更多关于构建这些示例的信息。

namedtuple是一个用于创建元组类的工厂函数 。 通过这个类,我们可以创建可以按名称调用的元组。

 import collections #Create a namedtuple class with names "a" "b" "c" Row = collections.namedtuple("Row", ["a", "b", "c"], verbose=False, rename=False) 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) 

namedtuples是一个很棒的功能,它们是数据的完美容器。 当你必须“存储”数据时,你会使用元组或字典,如:

 user = dict(name="John", age=20) 

要么:

 user = ("John", 20) 

字典的方法是压倒性的,因为字典是可变的,比元组慢。 另一方面,元组是不可变的,重量轻的,但是对于数据字段中的大量条目缺乏可读性。

namedtuples是两种方法的完美折中方案,具有很好的可读性,轻量级和不变性(加上它们是多态的!)。

什么是命名元组?

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

以下是您可以使用常规元组的方法:

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

你可以通过迭代解压缩来扩展一个元组:

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

命名元组是元组可以通过名称而不是索引来访问它们的元素!

你这样做一个namedtuple:

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

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

当它改进你的代码以使你的代码中表达元组元素的语义时使用它们。 您可以使用它们而不是对象,否则将使用具有不变数据属性和功能的对象。 您也可以对它们进行子类化以添加功能,例如 :

 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.3fy=%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 

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.saturation,而不是p[1] 。 这很容易理解。 它看起来更干净。

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

  1. 正如刚才所说的,这个名称使得元组更容易理解。 所以如果你需要引用元组中的项目,那么将它们创建为namedtuples就是合理的。
  2. 除了比词典更轻量级之外,namedtuple还保持着与字典不同的顺序。
  3. 如上例所示,创建namedtuple的实例比字典更简单。 引用指定元组中的项目看起来比字典更清晰。 p.hue而不是p['hue']

语法

 collections.namedtuple(typename, field_names[, verbose=False][, rename=False]) 
  • namedtuple在收藏库中。
  • typename:这是新的元组子类的名称。
  • field_names:每个字段的名称序列。 它可以是一个列表['x', 'y', 'z']或字符串xyz (没有逗号,只是空格)或x, y, z一个序列。
  • 重命名:如果重命名为True ,则无效的字段名会自动替换为位置名称。 例如, ['abc', 'def', 'ghi','abc']被转换为['abc', '_1', 'ghi', '_3'] ,从而消除关键字'def'是定义函数的保留字)和重复的字段名称'abc'
  • 详细:如果verbose为True ,则在构建之前打印类定义。

如果你愿意的话,你仍然可以通过他们的位置访问namedtuples。 p[1] == p.saturation 。 它仍然像一个普通的元组解包。

方法

所有常规的元组方法都被支持。 例如:min(),max(),len(),in,not in,concatenation(+),index,slice等等。另外还有一些用于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并且有_replace方法。 _replace产生一个new实例; 它不会修改原来的值或替换旧的值。 你当然可以把新的结果保存到变量中。 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

什么是named?

顾名思义,namedtuple是一个名字的元组。 在标准元组中,我们使用索引访问元素,而namedtuple允许用户为元素定义名称。 这是非常方便的,尤其是处理CSV(逗号分隔值)文件和处理复杂和大型的数据集,其中的代码变得杂乱的索引使用(而不是pythonic)。

如何使用它们?

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

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

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