Python中的“命名元组”是什么?
-
24-10-2019 - |
题
阅读 Python 3.1的变化, ,我发现了一些……意外:
sys.version_info元组现在是 命名为Tuple:
我以前从未听说过命名的元组,我认为元素可以被数字(例如在元组和列表中)或键(如dicts中)索引。我没想到他们可以双向索引。
因此,我的问题是:
- 什么命名为元组?
- 如何使用它们?
- 为什么/什么时候应该使用命名元素而不是普通元组?
- 为什么/何时应该使用普通元素而不是命名元素?
- 是否有任何“命名列表”(命名元组的可变版本)?
解决方案
命名的元组基本上是易于创建的,轻巧的对象类型。可以使用类似对象的变量解码或标准元组语法引用命名的元组实例。它们可以与 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
因此, 您应该使用名为“元组”而不是元组,任何您认为对象符号都会使您的代码更加蓬松和更容易读取. 。我个人已经开始使用它们来表示非常简单的价值类型,尤其是在将它们作为参数传递给函数时。它使功能更具可读性,而无需看到元组包装的上下文。
此外, 您也可以替换普通 不变 没有功能的课程, ,只有他们的字段。您甚至可以将命名的元组类型用作基类:
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
但是,我不知道有任何形式的“命名列表”可以让您添加新字段。您可能只想在这种情况下使用字典。可以使用命名的元组转换为词典 pt1._asdict()
返回 {'x': 1.0, 'y': 5.0}
并且可以使用所有常用的字典函数进行操作。
如前所述,您应该 检查文档 有关这些示例的更多信息。
其他提示
名为tuple 是一个 工厂功能 为了做元组课。在该课程中,我们可以创建可以按名称调用的元组。
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)
什么命名为元组?
一个名叫元组是元组。
它可以完成元组。
但这不仅仅是一个元组。
这是元组的特定子类,该子类是通过编程为您的规范创建的,带有命名字段和固定长度。
例如,这还创建了一个元组的子类,除了固定长度(在这种情况下为三个)外,可以在任何地方使用元组,而无需断裂。这被称为liskov替代性:
>>> 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'
您可以用凹陷的包装扩展元组:
>>> 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)
为什么/何时应该使用普通元素而不是命名元素?
从使用命名的元组切换到元组可能是回归。前期设计决策围绕涉及额外代码的成本是否值得在使用元组时提高可读性。
命名元组与元组没有使用的额外记忆。
是否有任何“命名列表”(命名元组的可变版本)?
您正在寻找实现静态尺寸列表的所有功能的插槽对象,也可以寻找像名为tuple一样工作的子分类列表(并且以某种方式阻止了列表的变化大小。)
现在扩展的,甚至可能替代了第一个的例子:
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
名称Tuples是一个很棒的功能,它们是数据的理想容器。当您必须“存储”数据时,您将使用元组或字典,例如:
user = dict(name="John", age=20)
或者:
user = ("John", 20)
词典方法是压倒性的,因为dict比元素较可变且慢。另一方面,这些元组是不变的,轻巧的,但缺乏数据字段中大量条目的可读性。
命名为两种方法的理想折衷方案,具有出色的可读性,轻巧和不变性(此外它们是多态性的!)。
命名元组允许向后兼容,以检查这样的版本
>>> sys.version_info[0:2]
(3, 1)
通过使用此语法,允许将来的代码更加明确
>>> sys.version_info.major
3
>>> sys.version_info.minor
1
名为tuple
是清理代码并使其更可读的最简单方法之一。它可以自我记录元组中发生的事情。命名为“实例”的内存效率与常规元素一样有效,它们的记忆效率与没有定义词典一样,使其比词典更快。
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"
很难理解第一个示例中发生的事情。使用一个名为Tuple,每个字段都有一个名称。然后您通过名称而不是位置或索引访问它。代替 p[1]
, ,我们可以称其为P.饱和。这更容易理解。而且看起来更干净。
创建名为tuple的实例比创建字典更容易。
# 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
您什么时候可以使用名为Tuple
- 正如刚才所说的那样,命名的tuple使理解元组变得容易得多。因此,如果您需要参考元组中的项目,则将其创建为命名图是有意义的。
- 除了比字典更轻巧外,名为Tuple还保留了与词典不同的订单。
- 如上面的示例,创建一个名称tuple的实例比字典更简单。并在命名元组中引用该物品看起来比字典更干净。
p.hue
而不是p['hue']
.
语法
collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
- 名为Tuple在收藏库中。
- TypName:这是新元组子类的名称。
- field_names:每个字段的名称序列。它可以是列表中的序列
['x', 'y', 'z']
或字符串x y z
(没有逗号,只有空格)或x, y, z
. - 重命名:如果重命名为
True
, ,无效的字段名称将自动替换为位置名称。例如,['abc', 'def', 'ghi','abc']
被转换为['abc', '_1', 'ghi', '_3']
, ,消除关键字'def'
(因为那是定义函数的保留词)和重复的字段名称'abc'
. - 冗长:如果冗长为
True
, ,班级定义是在构建之前打印的。
如果您选择的话,您仍然可以通过其位置访问命名。 p[1] == p.saturation
. 。它仍然像普通元组一样打开包装。
方法
所有 常规元组方法 得到支持。例如:min(),max(),len(),in,inon,in,inn,insteenation(+),索引,切片等,还有一些额外的命名tuple。注意:这些都以下划线开始。 _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
产生a 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
返回新的 命令 哪个将字段名称映射到其相应的值。
语法
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
什么名字tuple?
顾名思义,名为Tuple是一个带有名称的元组。在标准元组中,我们使用索引访问元素,而名为Tuple允许用户定义元素的名称。这非常方便,尤其是处理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中,有很好的使用称为命名元组的容器,可用于创建类的定义,并具有原始元组的所有功能。
使用命名元组将直接应用于默认类模板以生成一个简单的类,此方法允许大量代码提高可读性,并且在定义类时也非常方便。
使用名为Tuple的另一种方法(一种新方法)是使用命名tuple从键入软件包中使用: 键入命名tuple中的提示
让我们以这篇文章中最高答案的示例来查看如何使用它。
(1)在使用命名元组之前,代码就是这样:
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)
(2)现在我们使用命名元组
from typing import NamedTuple, Number
继承名为Tuple类并在新类中定义变量名。测试是班级的名称。
class test(NamedTuple):
x: Number
y: Number
从类创建实例并将值分配给他们
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)
尝试这个:
collections.namedtuple()
基本上, namedtuples
轻巧的对象类型很容易创建。他们将元组变成方便的容器,以进行简单任务。和 namedtuples
, ,您不必使用整数索引来访问元组成员。
例子:
代码1:
>>> from collections import namedtuple
>>> Point = namedtuple('Point','x,y')
>>> pt1 = Point(1,2)
>>> pt2 = Point(3,4)
>>> dot_product = ( pt1.x * pt2.x ) +( pt1.y * pt2.y )
>>> print dot_product
11
代码2:
>>> from collections import namedtuple
>>> Car = namedtuple('Car','Price Mileage Colour Class')
>>> xyz = Car(Price = 100000, Mileage = 30, Colour = 'Cyan', Class = 'Y')
>>> print xyz
Car(Price=100000, Mileage=30, Colour='Cyan', Class='Y')
>>> print xyz.Class
Y
其他所有人都已经回答了,但是我认为我还有其他东西要添加。
名为Tuple可以直观地认为是定义类的快捷方式。
查看一种繁琐而传统的方式来定义 class
.
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'