文章目录

Python3 中的 namedtuple(命名元组)

  • namedtuple的概念与应用
  • 概念
  • 应用
  • namedtuple的基本使用
  • 基本使用方法
  • 字段名列表的其他表示形式
  • namedtuple`实例类型名`与`变量名`通常写成相同,但也可写成不同,注意它们的区别
  • namedtuple的方法
  • `_fields`属性(字段)
  • `_make(iterable)`方法(从一个可迭代对象中创建一个`namedtuple`)
  • `_asdict()`方法(转换成有序字典OrderedDict)
  • 关于有序字典OrderedDict
  • namedtuple与字典比较

Python3 中的 namedtuple(命名元组)

在python3中,有两种方法可以定义出类似于C/C++中的结构体,一种是类,另一种是namedtuple。

在Python中,collections.namedtuple是一个生成可以使用名称来访问元素的元组子类的工厂函数。namedtuple提供了一种定义简洁、不可变的数据类的方式。

namedtuple的概念与应用

概念

在Python的标准库collections模块中,有一个非常有用的工具 - namedtuple,它可以创建一个包含名称的元组。这是一种特殊的元组,不仅可以使用索引来访问元素,也可以通过名字来访问元素。

参考文章:Python Docs: collections.namedtuple

应用

举个例子,如果你有一个点的坐标,你可以像下面这样表示:

p = (1.0, 2.0)

然后你需要通过索引才能访问元素:

x = p[0]
y = p[1]

但如果使用namedtuple,你就可以通过名称来访问元素,代码更清晰:

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(1.0, 2.0)
x = p.x
y = p.y

namedtuple的基本使用

基本使用方法

首先,要使用namedtuple,需要从collections模块导入它。然后,使用namedtuple函数创建一个新的元组子类。这个函数有两个参数:一个是类的名称,另一个是类的字段名。

from collections import namedtuple

# 创建一个名为'Person'的namedtuple,它有'name'和'age'两个字段
Person = namedtuple('Person', ['name', 'age'])

然后你可以像使用普通的类一样使用这个namedtuple:

# 创建一个Person对象
bob = Person(name='Bob', age=30)

# 访问其字段
print(bob.name)  # 输出: Bob
print(bob.age)   # 输出: 30

字段名列表的其他表示形式

在定义namedtuple时,字段名列表可以用多种形式来表示。例如,除了像上面那样直接传递一个列表外,还可以传递一个包含空格分隔的字段名的字符串:

Person = namedtuple('Person', 'name age')

或者,也可以传递一个由逗号分隔的字段名构成的字符串:

Person = namedtuple('Person', 'name, age')

这些方式创建出来的namedtuple都是一样的。

namedtuple实例类型名与变量名通常写成相同,但也可写成不同,注意它们的区别

注意看下面代码,Person是变量名,PersonXXX是实例类型名,它们通常写成相同,但这次我们写成不同,看看结果有何差异:

from collections import namedtuple

# 创建一个名为'Person'的namedtuple,它有'name'和'age'两个字段
Person = namedtuple('PersonXXX', ['name', 'age'])

# 创建一个Person对象
bob = Person(name='Bob', age=30)

# 访问其字段
print(bob.name)  # 输出: Bob
print(bob.age)   # 输出: 30

# 打印实例类型
print(type(bob))    # 输出:<class '__main__.PersonXXX'>

# 打印实例
print(bob)  # 输出:PersonXXX(name='Bob', age=30)

在namedtuple中,第一个参数('PersonXXX'在此例中)是生成的新类的名称。这个名称主要有两个作用:

  1. 它会决定这个namedtuple实例的类型名称。例如,在你的代码中,如果我们使用Python内置的type()函数检查bob的类型,它将返回PersonXXX而不是Person。
print(type(bob))  # 输出: <class '__main__.PersonXXX'>
  1. 在打印namedtuple对象时,它将出现在输出中。如果你直接打印bob,你会看到:
print(bob)  # 输出: PersonXXX(name='Bob', age=30)

然而,尽管我们在创建namedtuple时给出的类名是PersonXXX,我们仍然可以使用Person(变量名)来创建这个namedtuple的实例。这是因为我们将namedtuple赋值给了Person这个变量。

这就意味着,虽然’PersonXXX’在代码中的可见度较低,但它确实对namedtuple的行为产生了影响。这也是为什么通常我们建议让namedtuple的名称和它的变量名相同,以避免混淆。

namedtuple的方法

namedtuple生成的类有一些特殊的方法和属性。

_fields属性(字段)

_fields属性是一个包含所有字段名的元组:

Person = namedtuple('Person', 'name age')
print(Person._fields)  # 输出: ('name', 'age')

_make(iterable)方法(从一个可迭代对象中创建一个namedtuple)

_make()方法可以从一个可迭代对象中创建一个namedtuple:

Person = namedtuple('Person', 'name age')
p = Person._make(['Bob', 30])
print(p)  # 输出: Person(name='Bob', age=30)
  • 区别p = Person._make(['Bob', 30]) 和 bob = Person(name='Bob', age=30) 这两种方式的主要区别在于它们的参数形式和实际应用场景:
  1. Person._make(['Bob', 30]): _make() 是一个类方法,接受一个可迭代对象作为参数,比如列表或元组。这个方法会按照 Person 类定义的字段顺序从可迭代对象中取值。这种方式特别适合在你已经有一个包含所有字段值的列表或者元组时使用。
  2. Person(name='Bob', age=30): 这是最常见的创建类实例的方式,即直接调用类的构造函数,通过关键字参数指定每个字段的值。这种方式更加直观明了,因为你可以清楚地看到每个字段对应的值。

这两种方式没有绝对的好坏之分,只是根据不同的情况选择最适合的一种。

_asdict()方法(转换成有序字典OrderedDict)

_asdict()方法返回一个将字段名映射到其对应值的新OrderedDict:

Person = namedtuple('Person', 'name age')
bob = Person(name='Bob', age=30)
d = bob._asdict()
print(d)  # 输出: OrderedDict([('name', 'Bob'), ('age', 30)])
关于有序字典OrderedDict

OrderedDict 是Python中的一个数据类型,它是字典(dict)的一个子类,用于创建保持元素插入顺序的字典。

在Python 3.7之前,普通的字典并不保证键值对的插入顺序。这意味着当你遍历一个字典时,键值对的输出顺序可能与你插入的顺序不同。OrderedDict就是为了解决这个问题而出现的,它会按照键值对首次插入的顺序进行排序。

然而,从Python 3.7开始,Python中的内置字典已经默认保持插入顺序了,所以在大多数情况下,你可能并不需要OrderedDict。

不过,OrderedDict还有一些标准字典没有的特性,比如 popitem(last=True) 方法可以弹出最后一个插入的元素(如果设置 last=False 则弹出第一个插入的元素),这在某些情况下可能会很有用。

这是一个简单的 OrderedDict 使用示例:

from collections import OrderedDict

d = OrderedDict()
d['a'] = 1
d['c'] = 3
d['b'] = 2

for key, value in d.items():
    print(key, value)
# 输出:
# a 1
# c 3
# b 2

可以看到,输出的顺序与插入的顺序一致。

namedtuple与字典比较

虽然字典也可以用于存储具有名称的数据,但namedtuple在某些情况下更胜一筹。首先,namedtuple占用的内存更少。其次,作为元组的子类,namedtuple是不可变的,而字典是可变的。此外,namedtuple还可以通过点号.来访问数据,而字典则需要使用方括号[]。