3

Python 中 NamedTuple 的理解

 2 years ago
source link: https://www.lfhacks.com/tech/python-namedtuple/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python 中 NamedTuple 的理解

2021-12-04
阅读 4 分钟
阅读量 98
扫一扫,转发文章

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) # 可遍历...1234>>> A,B,C,D = t1 # 可拆包>>> A1>>>

因为 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 形式:

>>> t1T(a=1, b=2, c=3, d=4)>>> t1._asdict(){'a': 1, 'b': 2, 'c': 3, 'd': 4}>>>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK