namedtuple(具名元组)和与 tuple 通过坐标位置辨认属性相比,可读性更好。
namedtuple(具名元组)自带属性名称,对比下面两个:
t1 = ('Alice', 12500, 2)
t2 = Employee(name='Alice', salary=12500, grade=2)
namedtuple 可以使用在 tuple 的任何地方。
你可以这么理解:namedtuple 之于 tuple 就像 dict 之于 list,为每个元素起了名字,使用名字操作元素,更加方便、好记。
使用场景
当你打算抽象出一个类,而你设计的这个类只有属性而没有方法,仅仅为了保存数据,而且不做进一步修改。那么你可以考虑使用 namedtuple,将大大减少你的代码量。
简单示例
下面的几行代码简单展示了 namedtuple 的基本用法:
t
变量指向一个新生成的类,类名叫做 T
>>> t=collections.namedtuple('T',["a","b","c","d"])
实例化出 T 的对象,叫 t1
>>> t1=t(a=1,b=2,c=3,d=4)
>>> print(t1)
T(a=1, b=2, c=3, d=4)
参数不够就报错
>>> t1=t(a=1, b=2, c=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'd'
如何访问实例的成员
>>> t1.a # 点号表示法
1
>>> t1[1] # 可索引
2
>>> for item in t1: print(item) # 可遍历
...
1
2
3
4
>>> A,B,C,D = t1 # 可拆包
>>> A
1
>>>
因为 namedtuple 从 tuple 派生而来,所以继承了 tuple ,乃至 sequence 的特性。
上面例子里的点号表示法,十分方便,因为 dict 类型并不支持这种访问成员的方法。
工厂函数 namedtuple()
之所以叫工厂函数,是因为它不返回具体的实例,而是动态生成一个新的类,可以用这个新类来创建实例。
为什么要使用工厂函数而不是直接提供一个类?因为 namedtuple 要让用户自定义类型的名称,这个名称不能预先定义,所以要先动态生成一个类,再用这个类生成自定义类型的tuple实例。
与之相比,其他数据类型都从同一个类,实例化而来:
>>> d = dict(a=1, b=2)
>>> d
{'a': 1, 'b': 2}
namedtuple() 的参数列表
namedtuple(typename,
field_names,
*,
rename=False,
defaults=None,
module=None
)
下面逐个解释参数(单独星号 *
的含义,见 这篇文章)。
类名 typename
必选参数。定义了生产出的类的名称,也就是新生成 tuple 的 type,所以叫 typename. 看下面的例子:
>>> import collections
>>> new_class = collections.namedtuple('aa',['a'])
>>> new_class.__class__
<class 'type'> # 从type派生出来的
>>> new_class.__name__ # 类的名称
'aa'
这里创建了 aa
这个新类,但是这个新类被 new_class
变量所引用。就像
x=2
在使用 x
这个变量时,就在指代 2
这个字面量。当我使用 new_class
变量时,实际上在指代 aa
这个新类。下面我们用这个新类来创建对象。
>>> aa_object = new_class(a=1)
>>> aa_object.__class__
<class '__main__.aa'> # 从 aa 实例化
>>> type(aa_object)
<class '__main__.aa'> # aa 类型
>>> isinstance(aa_object,new_class)
True
>>>
很多关于 namedtuple 的教程里,喜欢把工厂函数 namedtuple()
返回的类赋给同名的变量,比如
Card = collections.namedtuple('Card', ['rank', 'suit'])
或者
Employee = collections.namedtuple('Employee', ['name', 'city', 'salary'])
这样会引起误解,会以为对变量名又什么要求。其实并没有, 只是指向新类的一个变量而已。比如下面的例子中,更换了变量名称。
>>> new_2_class = new_class
>>> new_2_class(a=1).__class__
<class '__main__.aa'>
>>>
我们用新的变量指代,仍然指向同一个类。对于 Python 变量的赋值,可以参考 这篇文章
字段列表 field_names
定义 tuple 的字段名称,字段列表有序列和字符串两种形式:
字符串的序列
sequence of string 即可,比如
namedtuple('T',["a","b","c","d"])
namedtuple('T',("a","b","c","d"))
都可以
空格或逗号分隔
为了方便,还可以将所有字段名称放在同一个长的字符串里,用空格或者逗号分隔。比如:
namedtuple('T',"a b c d"])
namedtuple('T',"a,b,c,d")
非法的字段名称
python 的预留关键字不能用作字段名称:
>>> collections.namedtuple('T',"for,b,c,d")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "xx.py", line 393, in namedtuple
raise ValueError('Type names and field names cannot be a '
ValueError: Type names and field names cannot be a keyword: 'for'
字段名称不能以下划线开头 (_),因为下划线有其他预留的用途。
rename
非必选参数,布尔值。True 表示允许自动重命名,(上节)[#invalid] 中非法字段名会被自动改成下划线开头的字段名(原因在 这一节 中有解释)。还是用 (上节)[#invalid] 的例子:
>>> collections.namedtuple('T',"for,b,c,d",rename=True)._fields
('_0', 'b', 'c', 'd')
# for 被自动更名为 _0
defaults
注意末尾有个"s",非必选参数,各字段的默认值。接受的取值是可迭代对象(iterable)。比如
>>> t=collections.namedtuple('T',"a,b,c,d") # 没有定义默认值
>>> t() #报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 4 required positional arguments: 'a', 'b', 'c', and 'd'
>>> t=collections.namedtuple('T',"a,b,c,d",defaults=[1,2,3,4]) # 定义了默认值
>>> t() #自动赋值
T(a=1, b=2, c=3, d=4)
>>>
如果 defaults 提供了 dict 类型,那么默认值取自 key :
>>> t=collections.namedtuple('T',"a b c d",defaults={"a":4,"b":3,"c":2,"d":1})
>>> t()
T(a='a', b='b', c='c', d='d')
>>>
如果 defaults 提供了 str 类型,是每个字母分别赋值:
>>> t=collections.namedtuple('T',"a b c d",defaults="1234")
>>> t()
T(a='1', b='2', c='3', d='4')
对不上的情况
如果 defaults 提供的数量和前面 fields 的数量对不上,那么一定有些字段是需要在实例化的时候靠位置定义的,比如说:
>>> t=collections.namedtuple('T',"a b c d",defaults="34")
default 提供的数值少两个,那么实例化的时候,必然有两个参数需要用位置来赋值、
又因为位置参数必须先于关键字参数,所以 defaults 值是从右往左一一赋值。
>>> t=collections.namedtuple('T',"a b c d",defaults="34")
>>> t(1,2)
T(a=1, b=2, c='3', d='4')
>>>
自带函数
namedtuple 是派生自 tuple 的子类,除了继承了 tuple 的所有特性外,还自带了一些特有的方法方便使用。
为了避免和用户自定义的属性冲突,自带的方法名都以下划线开头,这也是为什么 fields 字段不能使用下划线开头的字段名的原因。
_make()
输入一个可迭代对象,可以实例化一个对象。我觉得没什么用,直接用类名实例化即可。如果数据保存在序列里,那么用星号 unpack 即可。
>>> t=collections.namedtuple('T',"a b c d")
>>> t(*[1,2,3,4])
T(a=1, b=2, c=3, d=4)
>>>
_asdict()
输出一个 dict 形式:
>>> t1
T(a=1, b=2, c=3, d=4)
>>> t1._asdict()
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>>
如果您对本文有疑问或者寻求合作,欢迎 联系邮箱 。邮箱已到剪贴板
精彩评论
本站 是个人网站,采用 署名协议 CC-BY-NC 授权。
欢迎转载,请保留原文链接 https://www.lfhacks.com/tech/python-namedtuple/ ,且不得用于商业用途。