Python数据类构建器(二)

本文介绍了Python中的namedtuple用于创建带命名属性的元组,typehints用于类型提示增强代码可读性,以及dataclass的使用,包括其可变性和默认值策略。还讨论了如何使用default_factory避免默认值共享带来的问题和初始化后处理的__post_init__方法。
摘要由CSDN通过智能技术生成

1、典型的具名元祖namedtuple

collections.namedtuple是一个工厂函数,用于构建增强的tuple子类,具有字段名称,类名等属性。namedtuple构建的类可在任何需要元祖的地方使用。甚至以前Python标准库中发挥元祖的很多函数,现在都返回具名元祖,这对用户的代码没有任何影响,可读性也大大提高。

namedtuple关键的类,其实例占用的内存与元祖相同,因为字段名存储在类中。

from collections import namedtuple

LOL = namedtuple("LOL", 'name region skill job')

yasuo = LOL('亚索', '艾欧尼亚', '中路', '战士/刺客')
print(yasuo)  # LOL(name='压缩', region='艾欧尼亚', skill='中路', job='战士/刺客')
print(yasuo.job)  # 直接访问属性 战士/刺客
print(yasuo[1])  #  也可通过索引访问 艾欧尼亚

创建namedtuple需要指定两个参数:一个类名和一个字段名称列表。后一个参数可以是产生字符串的可迭代对象,也可以是一整个以空格分隔的字符串。

除了从tuple继承,namedtuple还有几个额外的属性和方法。下面示例演示了几个最有用的属性和方法:类属性_fields,类方法_make(iterable)和实例方法_asdict(),这些在上期内容中也提到过。

from collections import namedtuple

LOL = namedtuple("LOL", 'name region skill job game', defaults=['英雄联盟'])  # 也可以设置字段默认值

yasuo = LOL('亚索', '艾欧尼亚', '中路', '战士/刺客')
print(yasuo._fields)  # 属性值是一个元组,存储类的字段名称 输出:('name', 'region', 'skill', 'job')
print(yasuo._asdict())  # 输出: {'name': '亚索', 'region': '艾欧尼亚', 'skill': '中路', 'job': '战士/刺客', 'game': '英雄联盟'}
jie_data = ('劫', '均衡教派', '中路', '刺客', '英雄联盟')
jie = LOL._make(jie_data)  # _make方法根据可迭代对象构建city实例
print(jie)  # 输出:LOL(name='劫', region='均衡教派', skill='中路', job='刺客')
print(jie._asdict())  # 返回根据具名元组实例构建的dict对象,输出{'name': '劫', 'region': '均衡教派', 'skill': '中路', 'job': '刺客'}

2、带类型的具名元组typing.NamedTuple

在上期也讲过这个构建类的使用,这里也继续补充,虽然说类型提示用来声明函数参数、返回值、变量、属性的预期类型,但是Python字节码编译器和解释器根本不会强制你提供类型信息,类型提示对程序运行没有任何影响。

import typing

class LOL(typing.NamedTuple):
    name: str  # 设置每个属性的类型
    region: str
    skill: int
    job: str = "中路"  # 指定字段job的默认值“中路”

yasuo = LOL('亚索', "艾欧尼亚", skill=15)
jie = LOL('劫', '均衡教派', 14, job="刺客")
print(yasuo)  # LOL(name='亚索', region='艾欧尼亚', skill=15, job='中路')
print(jie)  # LOL(name='劫', region='均衡教派', skill=14, job='刺客')
print(typing.get_type_hints(LOL))  # {'name': <class 'str'>, 'region': <class 'str'>, 'skill': <class 'int'>, 'job': <class 'str'>}
print(jie._fields)  # 获取字段名称 ('name', 'region', 'skill', 'job')
print(jie._asdict()) # 返回dict对象 {'name': '劫', 'region': '均衡教派', 'skill': 14, 'job': '刺客'}

3、@dataclass详解

dataclass是我目前使用最多,个人认为最好的数据类构建器,以装饰器的形式,可读性高,功能也强大,当然也仅限个人观点哈!我们一般使用的@dataclass都比较简单,很多情况下直接拿来用就行了。但其实这个装饰器接收多个关键字参数,完整前面如下:

def dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False,
              unsafe_hash=False, frozen=False, match_args=True,
              kw_only=False, slots=False, weakref_slot=False):

大概介绍几个重要参数:

  • init:用于生成__init__,如果用户实现了__init__,则忽略此参数。
  • repr:用于生成__repr__,如果用户实现了__repr__,则忽略此参数。
  • eq:用于生成__eq__,如果用户实现了__eq__,则忽略此参数。
  • order:用于⽣成 __lt__、__le__、__gt__、 __ge__,设为True时,允许排序数据类的实例,但如果eq=false,或者自行定义或继承其他用于比较的方法,则抛出异常
  • frozen:让实例不可变,防止意外更改,相对安全,但是不是绝对不可比变,因为Python对象是动态的,只要愿意,程序员还是可以绕过这道防线。

 简单用法:

@dataclass(frozen=True)  # 不可变实例
class Student:
    name: str
    sex: int


s = Student(name='jay', sex=1)
print(s.name)  # jay
print(s.sex)  # 1
s.name = "jjj"  # 报错dataclasses.FrozenInstanceError: cannot assign to field 'name'

字段选项:

对于初级Python开发人员来说,可变的默认值往往会引发bug。如果在函数定义中使用可变的默认值,调用函数时如果一不小心很容易破坏默认值,甚至导致代码运行出现bug。类属性通常用做实例属性的默认值,数据类也是如此。@dataclass使用类型提示中的默认值生成传给__init__方法的参数默认值。为了避免bug,@dataclass无法像下面那样定义类。

from dataclasses import dataclass

@dataclass
class Classes:
    name: str
    students: list = []
# 报错:ValueError: mutable default <class 'list'> for field students is not allowed: use default_factory

幸运的是,错误消息为我们提供了一个解决方案:使用default_factory:

from dataclasses import dataclass,field
@dataclass
class Classes:
    name: str
    students: list = field(default_factory=list)

default_factory参数的值可以是一个函数、一个类,或者其他可调用对象,在每次创建数据类的实例时调用(不带参数),构建默认值。这样,每个Classes实例都有自己的一个list,而不是所有实例共用一个list,共用往往容易导致bug,而且实际业务场景中很少希望共用。

初始化后处理:

@dataclass生成的__init__方法只做一件事:即把传入的参数及默认值赋值给实例属性,变成实例字段。可是有些时候,初始化实例要做的不止这些,为此,可以提供一个__post_init__方法。如果存在这个方法,则@dataclass将在生成__init__方法最后调用__post_init__方法。__post_init__方法通常用于执行验证,以及根据其他字段计算一个字段的值。

Python数据类构建器就到这了,感兴趣的可以去看官方文档哈。

 

 

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值