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 "", line 1, in TypeError: () 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__ # 从type派生出来的 >>> new_class.__name__ # 类的名称 'aa' 这里创建了 aa 这个新类,但是这个新类被 new_class 变量所引用。就像 x=2 在使用 x 这个变量时,就在指代 2 这个字面量。当我使用 new_class 变量时,实际上在指代 aa 这个新类。下面我们用这个新类来创建对象。 >>> aa_object = new_class(a=1) >>> aa_object.__class__ # 从 aa 实例化 >>> type(aa_object) # 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__ >>> 我们用新的变量指代,仍然指向同一个类。对于 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 "", line 1, in 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 "", line 1, in TypeError: () 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} >>>