说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的 namedtuple() 具名元组赋予每个位置一个含义,提供可读性和自文档性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。namedtuple 函数所创建(返回)的是一个 元组的子类,python 中基本数据类型都是类,且可以在buildins 模块中找到。
namedtuple 是一个 工厂函数,使用此函数可以创建一个可读性更强的元组。namedtuple() 位于 Python 内置模块 collections 中,可以使用以下方法使用:
from collections import namedtuple
# 声明
Student = namedtuple('Student', ['name', 'age', 'city'])
# 添加值,实例化
s = Student('Xiaoming', '19', 'Beijing')
# 使用索引访问
s[1] # 19
# 使用名称访问
s.name # 'Xiaoming'
# 使用 getattr()
getattr(s, 'city') # 'Beijing'
Python 的原生数据类型元组存在以下问题:
因此无法清晰地表达业务和现实事物的意义,不方便读取。
因此引入了工厂函数 collections.namedtuple ,构造一个带有字段名的元组。具名元组的实例和普通元组消耗的内存一样多,因为字段名都被存在对应的类里面。这个类跟普通的对象实例比起来也要小一些,因为 Python 不会用 __dict__
来存放这些实例的属性。具名元组和普通元组所需要的内存空间相同。
所以,具名元组的好处是:
那么为什么不用字典呢?
namedtuple() 位于 Python 内置模块 collections 中。
namedtuple(
typename,
field_names,
*,
rename=False,
defaults=None,
module=None,
)
有两个必填参数 typename 和 field_names,其中参数的意义如下:
typename
:元组子类名称,类型为字符串field_names
: 一个像 [‘x’, ‘y’] 一样的字符串序列,代表各元素的名称。也可以是一个纯字符串,用空白或逗号分隔开元素名,比如 'x y' 或者 'x, y' 。任何有效的 Python 标识符都可以作为字段名(除了下划线开头的那些内置方法名)。有效标识符由字母、数字、下划线组成,但首字母不能是数字或下划线,另外不能是关键词 keyword 比如 class, for, return, global, pass, 或 raise 。*
:独立星号,星号后面为命名关键字参数,不能按位置传入rename
:如果元素名称中含有 python 的关键字,则必须设置为 rename=True,无效字段名会自动转换成位置名。比如 ['abc', 'def', 'ghi', 'abc'] 转换成 ['abc', '_1', 'ghi', '_3'] , 消除关键词 def 和重复字段名 abc 。defaults
:可以为 None 或者是一个默认值的 iterable 。如果一个默认值域必须跟其他没有默认值的域在一起出现,defaults 就应用到最右边的参数。比如如果域名 ['x', 'y', 'z'] 和默认值 (1, 2) ,那么 x 就必须指定一个参数值 ,y 默认值 1 , z 默认值 2 。module
:如有定义,命名元组的 __module__
属性值就被设置。返回的是一个名为 typename 的元组子类。namedtuple() 是一个工厂函数,所谓工厂函数就是就是这个函数能生成一个新的子类,就像工厂生产商品一样。这个子类实例同样有文档字符串(类名和字段名)。
返回一个具名元组子类。例如:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
Point.__doc__ # 新类的 docstring
# 'Point(x, y)'
p = Point(11, y=22) # 用位置参数或关键字实例化
p[0] + p[1] # 像普通元组一样可索引
# 33
x, y = p # 像常规元组一样解包
x, y
# (11, 22)
p.x + p.y # 字段也可以通过名称访问
# 33
d = p._asdict() # 转换成字典
d['x']
# 11
Point(**d) # 从字典中转换
# Point(x=11, y=22)
p._replace(x=100) # 类似于 str.replace(),但目标是命名字段
# Point(x=100, y=22)
可以用以下方法创建 namedtuple() 构造子类:
Student = namedtuple('Student', ['name', 'age', 'city'])
Student = namedtuple('Student', 'name, age, city')
Student = namedtuple('Student', 'name age city')
Student.__doc__
# 'Student(name, age, city)'
# 支持可迭代对象,与有效标识标冲突可设置 rename
Student2 = namedtuple('Student',range(3), rename=True)
Student2.__doc__
# 'Student(_0, _1, _2)'
创建实例:
s = Student('Xiaoming', '19', 'Beijing')
s = Student(name='Xiaoming', age=19, city='Beijing')
s = Student._make(t) # 类方法, 从序列或迭代实例创建实例
Student._make(range(3)) # Student(name=0, age=1, city=2)
# 解包字典
d = {'name': 'Xiaoming', 'age': 19, 'city': 'Beijing'}
s = Student(**d)
s
# Student(name='Xiaoming', age=19, city='Beijing')
数据的读取:
s[1] # 19
s.name # 'Xiaoming'
getattr(s, 'city')
# 'Beijing'
# 读取所有字段名
s._fields
# ('name', 'age', 'city')
# 将实例内容转为字典
s._asdict()
# {'name': 'Xiaoming', 'age': 19, 'city': 'Beijing'}
# 转为元组
tuple(s)
# ('Xiaoming', 19, 'Beijing')
可以对实例值进行修改:
s = s._replace(age=20)
s
# Student(name='Xiaoming', age=20, city='Beijing')
其实是复制了一个新的实例,将部分值进行了修改。
somenamedtuple._field_defaults
是字典将字段名称映射到默认值。
Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
Account._field_defaults
# {'balance': 0}
Account('premium')
# Account(type='premium', balance=0)
可以用 somenamedtuple._fields
利用现有具名元组构建新的具名元组,例如:
Point = namedtuple('Point', ['x', 'y'])
Color = namedtuple('Color', 'red green blue')
Pixel = namedtuple('Pixel', Point._fields + Color._fields)
Pixel(11, 22, 128, 255, 0)
# Pixel(x=11, y=22, red=128, green=255, blue=0)
子类化对于添加和存储新的名字域是无效的。应当通过 _fields
创建一个新的命名元组来实现它:
Point3D = namedtuple('Point3D', Point._fields + ('z',))
因为一个命名元组是一个正常的Python类,它可以很容易的通过子类更改功能。
class Student(namedtuple('Student', 'name age city')):
__slots__ = ()
@property
def born_year(self):
return 2021 - int(self.age)
def __str__(self):
return f'Student: 我叫{self.name}, 生于{self.born_year}年。'
s = Student('小明', 18, 'Beijing')
s.born_year
# 2003
print(s)
# Student: 我叫小明, 生于2003年。
上面的子类设置 __slots__
为一个空元组。通过阻止创建实例字典保持了较低的内存开销。
__doc__
属性文档字符串可以自定义,通过直接赋值给 __doc__
属性:
Book = namedtuple('Book', ['id', 'title', 'authors'])
Book.__doc__ += ': Hardcover book in active collection'
Book.id.__doc__ = '13-digit ISBN'
Book.title.__doc__ = 'Title of first printing'
Book.authors.__doc__ = 'List of authors sorted by last name'
除了继承元组的方法,命名元组还支持三个额外的方法和两个属性。为了防止字段名冲突,方法和属性以下划线开始。这些额外方法和属性分别是:
方法:
s._make(iterable)
: 从序列或迭代实例创建一个新实例s._asdict()
: 返回一个新的 dict,将字段名称映射到它们对应的值s._replace(**kwargs)
: 返回新的命名元组实例,并将指定域替换为新的值属性:
s._fields
: 字符串元组列出了字段名,可用于从现有元组创建一个新的命名元组类型s._field_defaults
: 字典将字段名称映射到默认值示例如下:
from collections import namedtuple
Student = namedtuple('Student',
['name', 'age', 'city'],
defaults=('who', 'old', 'where'))
xiaoming = Student('Xiaoming', '19', 'Beijing')
daming_info = ('Daming', '20', 'Shanghai')
daming = Student._make(daming_info)
daming
# Student(name='Daming', age='20', city='Shanghai')
daming._asdict()
# {'name': 'Daming', 'age': '20', 'city': 'Shanghai'}
daming._replace(age=21, city='Shenzhen')
# Student(name='Daming', age=21, city='Shenzhen')
daming._fields
# ('name', 'age', 'city')
daming._field_defaults
# {'name': 'who', 'age': 'old', 'city': 'where'}
另外:本教程是基于 Python 3.9 的。
类型注解 typing.NamedTuple ,为命名元组添加类型提示的方法。 它还使用 class 关键字提供了一种优雅的符号:
class Component(NamedTuple):
part_number: int
weight: float
description: Optional[str] = None
⽤来构建只有少数属性但是没有⽅法的对象,⽐如数据库条⽬。
Employee = namedtuple('Employee', 'name, age, title')
import csv
for emp in map(Employee._make, csv.reader(open("employees.csv", "rb"))):
print(emp.name, emp.title)
Employee = namedtuple('Employee', 'name, age, title')
import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title FROM employees')
for emp in map(Employee._make, cursor.fetchall()):
print(emp.name, emp.title)
更新时间:2021-10-10 08:34:28 标签:python 元组 tuple