Author: 酱橙
参考文档:python.org
1. namedtuple
介绍:
collections.namedtuple
方法是针对tuple类型数据的一个工厂方法,并且为tuple的每个值添加对应的字段,增强了 tuple 数据的可读性。并且,普通tuple对应的操作,namedtuple
也可以执行,比如说tuple类型和 list类型的互相转化…等等。
推而广之,对于简单的类来说,可以通过 namedtuple
来直接生成。
具体可传入的参数:
collections.namedtuple(typename, field_names, *,
rename=False, defaults=None, module=None)
1.1参数说明:
typename: 根据传入的 typename 返回一个对应名字的 tuple的子类(一般将typename的值和返回接收的变量名书写成一样的), 并且对该子类实例化之后,可以通过 __repr__()
方法来查看实例对象的各个属性。
field_name 和 rename参数:
-
该字段接收的是一个序列,也可以接收一个通过逗号或者空格隔开的字符串:
'x y'
or'x, y'
。 -
需要注意的是,filed_name 字段遵循 Python 标准的命名规范,并且不能是关键字。因此,如果filed_name字段可能会出现Python内部使用的关键字的话,最好设置 rename = True。此时对应的 filed_name 会被自动重命名。
例如:
['abc', 'def', 'ghi', 'abc']
==>['abc', '_1', 'ghi', '_3']
default参数:
-
该参数的作用是给对应的变量添加默认值,传入的数据类型是一个序列,当然也可以不传,默认为
None
。 -
例如此时的file_name传入的参数为
['x','y','z']
,defaults传入的参数为(1,2)
,
因为默认参数得跟在普通形参的后面,因此时的 x默认值依旧为 0,y的默认值为1,z的默认值为2.
module 参数:
该类的 __module__
值将会被赋值为 该参数传入的值,一般用不着。
1.2占用内存大小:
- 这种命名的元祖所占的空间并不比普通元祖占的空间更多(没有对应的per-instance dictionaries 😵),具体不太理解。
参见原文:
Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.
- 所占的空间比 对应的字典要少很多:
测试:
import sys
import collections
a = {'name':"Tomsom",'age':12,'job':'Cooker'}
qwe = collections.namedtuple("MyTupleClass",['name','age','job'])
obj = qwe("Tomsom",12,'Cooker')
# ======
sys.getsizeof(a) # 240
sys.getsizeof(obj) # 72
sys.getsizeof(obj._asdict) # 64
1.3 示例:
import collections
MyTupleClass = collections.namedtuple("MyTupleClass",
['name','age','job'])
obj = MyTupleClass("Tomsom",12,'Cooker')
上面通过 namedTuple创建了一个 obj 的对象
对于 CSV 文件或者是 数据取读取方面,namedtupled也有其妙用:
import csv
EmployeeRecord = namedtuple('EmployeeRecord',
'name, age, title, department, paygrade')
# CSV:
for emp in map(EmployeeRecord._make,
csv.reader(open("employees.csv", "rb"))):
print(emp.name, emp.title)
# SQL:
import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade '
'FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
print(emp.name, emp.title)
1.4 常用的几个方法:(骚操作)
为了防止自带的方法与属性发生冲突的可能性,每个方法前面都有一个 单下划线,内置的共包含三个方法,两个属性:
classmethod somenamedtuple._make
(iterable)
- 将序列实例化为当前类的实例对象。
>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
somenamedtuple._asdict
()
>>> p = Point(x=11, y=22)
>>> p._asdict()
{'x': 11, 'y': 22}
-
把当前对象转化为一个字典【感觉可以用在反序列化上面】
-
需要注意的是在python 3.7的时候,返回的是
OrderedDict
数据类型,在Python3.8的时候,返回的则是一个普通的dict
数据类型。
somenamedtuple._replace
(**kwargs)
- 修改对应实例的参数,修改的时候会新开辟一个空间,因此需要赋值回原来的内存空间.
示例:
>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)
for partnum, record in inventory.items():
inventory[partnum] = record._replace(price=newprices[partnum],
timestamp=time.now())
somenamedtuple._fields
查看当前对象/类有哪些属性。这个操作可以结合 mixin的编程思想。
示例:
>>> p._fields # view the field names
('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)
somenamedtuple._field_defaults
- 查看具有默认值的参数,返回的是一个字典。
Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
Account._field_defaults # {'balance': 0}
Account('premium') # Account(type='premium', balance=0)
1.5一些小技巧
1.5.1 获取属性的值
同样它也可以用 getattr()
来获取变量的值。
>>> getattr(p, 'x') # p 就是实例出来的对象
11
1.5.2 拆包实例化
当需要实例化一个对象的时候,可以通过字典拆包的方式把值传递进去。
>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)
1.5.3拆包获取属性值
>>> Point = namedtuple('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
1.5.4 创建子类:
因为 namedtupled 被认作一个Python的类,所以可以通过继承的方式来进行属性和方法的添加。下面的例子展示了 hypot 这个 property 属性和实例对象格式化的定义。
>>> class Point(namedtuple('Point', ['x', 'y'])):
# __slot__ 里面规定了实例字典里面包含的属性,
# 定义为空之后,实例对象的 __dict__ 即为空
# 作用就是减少每个实例对象占用的内存空间
... __slots__ = ()
... @property
... def hypot(self):
... return (self.x ** 2 + self.y ** 2) ** 0.5
... def __str__(self):
... return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
>>> for p in Point(3, 4), Point(14, 5/7):
... print(p)
Point: x= 3.000 y= 4.000 hypot= 5.000
Point: x=14.000 y= 0.714 hypot=14.018
Point: x= 3.000 y = 4.000 hypot = 5.000
Point: x=14.000 y = 0.714 hypot = 14.018
# 打印一下实例对象的 __dict__ 属性
Point(3,4).__dict__ # {}
通过上面的例子我们看到了如何通过一个 namedtuple
新建一个子类,
但是这种方式依旧比较麻烦,可以通过_fileds
属性来进行更简化的操作:
Point3D = collections.namedtuple('Point3D', Point._fields + ('z',))
然后查看 Point3D 这个类里面包含的属性:
Point3D._fields # ('x', 'y', 'z')
1.5.5 自定义属性及方法的doc说明
Point.__doc__ = 'Hardcover book in active collection'
Point3D.x.__doc__
然后查看一下对应属性的说明内容:
Point3D.x.__doc__ # '我是一个 x'
1.5.6 typing.namedtuple
在 typing
模块中也有一个 **namedtuple **方法,然后它是这么用的:
class Employee(NamedTuple):
name: str
eid: int
看起来是不是很奇怪,但是它等价于下面这种写法
Employee = collections.namedtuple('Employee', ['name', 'id'])
也就是说,习惯于 class
关键字的小伙伴有福音了,只要继承自NamedTuple,就可以调用 collections.namedtuple
里面的方法,并且具有相同的特性。
当然,如果需要给name和 eid 属性添加默认值,如下:
class Employee(NamedTuple):
name:str
eid:int = 3
e2 = Employee('Guido')
查看一下 e2的值:
e2.eid # 3
对于 NamedTuple
来说,就可以不使用 _doc_ 这个方法来添加注释了,直接往类里面扔就完事…
class Employee(NamedTuple):
"""Represents an employee."""
name: str
id: int = 3
def __repr__(self) -> str:
return f'<Employee {self.name}, id={self.id}>'
说 明:
当你使用NamedTuple,如果你需要查看类属性对应的变量类型的话,可以通过_filed_types
来获取(注意:这是collections.namedtuple里面没有的):
e2 = Employee('Guido')
e2._field_types # OrderedDict([('name', str), ('eid', int)])
但是在 Python 3.9 中,更推荐使用 __annotations__
这个方法(more standard):
e2.__annotations__ # OrderedDict([('name', str), ('eid', int)])
最后感谢女朋友的支持
此刻如果能有一杯奶茶就更好了:
