命名元组:namedtuple,是Python语言里没有引起足够重视的一种数据类型。这是Python中容易被忽略的神奇功能之一。
当需要定义一个类时,namedtuple是一个很好的选择。
那么命名元组以及它有什么特性?我们可以把namedtuples看作是Python内置的元组数据类型的扩展。
Python的元组是一种用于对任意对象进行分组的简单数据结构。元组的内容是不可变的,一旦被创建,数据元素就不能被修改。如下:
元组有一个缺点,存储在其中的数据只能通过索引来访问。你不能对存储在元组中的单个属性进行命名。这会影响代码的可读性。
而且,元组通常是一个ad-hoc结构。你很难确保两个元组存储有相同数量的字段和相同的属性。这使得当混合字段顺序时很容易引入“slip-of-the-mind”的bug。
Namedtuples解决的问题
Namedtuples主要用来解决如下两个问题。
首先,namedtuples和常规元组一样,它的内容是不可改变的。 一旦你将数据存储在其中,就不能再修改。
除此之外,命名元组也是,命名的元组。可以通过唯一的标识符来访问存储在其中的对象。您不必记住元素的下标索引,也不必为了记忆方便把索引定义为常量。
一个典型的命名元组如下所示:
使用命名元组前需要先导入collections模块。Python 2.6的标准库中即包含。在上面的例子里,定义了一个简单的命名元组数据类型:“Car”,包含“color” 和“mileage”两个字段。
也许你你会发现上面的语法有一点怪:为什么我们把两个字段写为一个字符串“color mileage”?
答案是因为命名元组的工厂函数会在字段字符串后默认调用split()函数对字段进拆解。所以上面的写法就是下面写法的快捷方式:
也可以直接把字段写成列表形式。这样写的优点是,把字段使用多行分隔,利于代码格式化。
接下来可以使用“Car”的工厂函数创建"car"对象了,就创建一个类对象“Car”,并且创建了参数值为“color” 和“mileage”的构造函数。
元组的解包以及带有*函数参数的解包也可正常工作。
命名元组中元素的即可以通过ID来访问,也可以通过其索引来访问。这样命名元组可以直接替代常规的元组。
命名元组的输出甚至可以是一个标准的字符串表达式,如下,这样省去了不少的输入冗余。
像元组一样,命名元组的内容也是不可修改的。当你试图重写其中字段值时,系统会抛出AttributeError异常:
在Python内部,命名元组是通过一个常规的类对象实现的。但是在内存使用效率上比常规类要高,和正常元组一样。
你可以把命名元组看做是一个自定义的不可修改的类的快捷方式,但是更节约内存。
命名元组的子类
因为命名元组是在常规类的基础上构建的,所以也可以添加方法到命名元组的类中。举个例子,你通过可以添加方法和属性扩展这个类,和扩展常规的类一样。例子如下:
我们可以创建对象MyCarWithMethods,并且定义一个 hexcolor()方法,结果如下
这个方法可能看起来有点笨拙。但是当你想得到一个不可修改的类时可以这么试一下。但是注意别搬起石头砸了自己的脚!
举个例子,由于命名元组的内部构建机制,添加一个新的不可改变的字段是比较麻烦的。最简单的方法就是利用基本元组的._field属性。
结果如下:
内置帮助函数
除了._fields属性,每个命名元组实例还内置了一些非常有用的帮助函数。这些函数都以下划线开头,通常用下划线开头表示它们是私有的方法或者属性,不属于类或者模块的公共接口。
在命名元组中这些以下划线命名的函数方法则有不同的含义:这些帮助函数或者属性是命名元组公共接口的一部分。之所以这么命名是为了避免和用户自定义的元组字段冲突。您可以随意使用这些帮助函数。
下面我会给大家展示一些十分有用的命名元组的帮助函数,我们以 _asdict() 函数为例,此函数返回值是一个字典,字典内容是命名元组中存储的元素,如下:
当你使用JSON样式的输出时,这个函数能避免输入错误,相当有用,举例如下:
另一个有用的帮助函数是 _replace() ,它会生成当前元组的一个副本(浅拷贝),并且允许用户对其中字段进行选择性替换。
最后,_make()类方法可以用来在序列或者迭代中创建一个新的命名元组实例,如下:
什么时候使用命名元组
命名元组是使代码变得简洁有效的一个简单快速的方法。它通过更好的结构化数据也增强了代码的可读性。
举个例子,我发现把有混合数据的字典这样的 ad-hoc数据类型用命名元组来代替,能够更清楚的表达我的意图。当我把数据作为命名元组重构时,代码中面临的一些问题,总能找到意想不到的解决办法。
使用命名元组处理非结构化的元组和字典,也会让我的同事们工作变的轻松,因为函数直接传递的数据是“自解释的”,非常容易理解(在某种程度上)。
另一方面,如果命名元组无法使您的代码变得更简洁,更易读,可维护性更高,我就不建议使用它们。因为“一件事太好的话可能就是坏事了”。
谨慎的使用命名元组的话,无疑会使您的代码更好,更富有表现力。
需要记住的事项
-
collection.namedtuple,是一个自定义的不可修改的类的快捷方式,但是更节约内存。
-
命名元组通过更容易让人理解的方式结构化您的数据,使您的代码更干净整洁。
-
命名元组提供了一些十分有用的以下划线开头帮助函数,您可以放心使用它们。