Python 数据类秘籍:优化程序效率的技巧与方法

🍀 前言

博客地址:

👋 简介

Python3.7引入了dataclass。dataclass装饰器可以声明Python类为数据类;数据类适合用来存储数据,一般而言它具有如下特征:

  • 数据类表示某种数据类型,数据对象代表一种特定类的实体,包含了实体的属性。
  • 同类型的对象之间可以进行比较;如,大于、小于或等于。

📖 正文

1 普通类与数据类的区别

以前定义一个类,我们可以使用以下的方式

class Car():
    def __init__(self, brand: str, price: float, color: str):
        self.brand = brand
        self.price = price
        self.color = color

    def __repr__(self):
        return f"Car(brand='{self.brand}', price={self.price}, color='{self.color}')"

    def __eq__(self, other):
        return (self.brand, self.price, self.color) == (other.brand, other.price, other.color)


if __name__ == '__main__':
    bmw = Car("宝马5系", 450000, "白色")
    bmw2 = Car("宝马5系", 450000, "白色")
    benz = Car("奔驰E级", 500000, "黑色")
    print(bmw)
    # Car(brand='宝马5系', price=450000, color='白色')
    print(benz)
	# Car(brand='奔驰E级', price=500000, color='黑色')
    print(bmw == bmw2)
    # True

现在通过@dataclass装饰器来表示数据类,进行简化

from dataclasses import dataclass

@dataclass(order=True)
class Car():
    brand: str
    price: float
    color: str


if __name__ == '__main__':
    bmw = Car("宝马5系", 450000, "白色")
    bmw2 = Car("宝马5系", 450000, "白色")
    benz = Car("奔驰E级", 500000, "黑色")
    print(bmw)
    # Car(brand='宝马5系', price=450000, color='白色')
    print(benz)
	# Car(brand='奔驰E级', price=500000, color='黑色')
    print(bmw == bmw2)
    # True

2 dataclass详解

2.1 装饰器dataclass

dataclass完整形式为@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) 其中True为生成对应方法,False将不生成;若类中已定义对应方法,则忽略此参数)

  • init:默认将生成__init__()方法
  • repr:默认将生成__repr__()方法;repr字符串包含类名、每个字段名称和其repr(按其类中定义顺序)
  • eq:默认将生成__eq__()方法;如果传入False,那么__eq__()方法将不会被dataclass添加,但会继承object.__eq__()(比较id)
  • order:默认不生成__gt__()__ge__()__lt__()__le__()方法
  • unsafe_hash:默认为False,则根据eq和frozen的设置方式生成__hash__()方法
    • 如果eq和frozen都为真,默认情况会生成一个__hash__()方法
    • 如果eq为真而frozen为假,则__hash__()将被设置为 None,将其标记为不可散列(确实如此,因为它是可变的
    • 如果eq为假,则__hash__()将保持不变,这意味着将使用超类的__hash__()方法(如果超类是object,将回退到基于id的散列)
  • frozen:如果该属性为为true,则对象初始化后属性将无法修改
2.2 field

field(*,default=MISSING,default_factory=MISSING,init=True,repr=True,hash=None,compare=True, metadata=None)

  • default:字段的默认值
  • default_factory:具有可变默认值的字段,必须是一个无参可调用对象;与default互斥(不可同时指定)
  • init:是否在__init__()方法中使用字段,默认值True
  • repr:是否在__repr__()方法中使用字段,默认值True
  • compare:是否在比较对象时, 包括该字段,默认值True
  • hash:计算hash时, 是否包括字段,默认值True
  • metadata:包含字段信息的映射
from dataclasses import dataclass, field

@dataclass
class Tire:
    """
    轮胎类
    """
    number: int = 4
    brand: str = '普利司通'


@dataclass(order=True)
class Car():
    brand: str
    price: float
    color: str
    # 引用默认长度 和 length: float = 5.10 等价
    length: float = field(default=5.10)
    # tire: str = Tire() 这种方式不起作用
    tire: str = field(default_factory=lambda: Tire())


if __name__ == '__main__':
    bmw = Car("宝马5系", 450000, "白色")
    benz = Car("奔驰E级", 500000, "黑色")
    print(bmw)
    # Car(brand='宝马5系', price=450000, color='白色', length=5.1, tire=Tire(number=4, brand='普利司通'))
    print(benz)
    # Car(brand='奔驰E级', price=500000, color='黑色', length=5.1, tire=Tire(number=4, brand='普利司通'))

备注

  • length: float = field(default=5.10)默认值同样可以写成length: float = 5.10
  • 默认值调用方法tire: str = field(default_factory=lambda: Tire()),tire: str = Tire() 这种方式不起作用

3 数据类的使用

3.1 不可变数据类型

首先我们将dataclass注解中frozen的属性的默认值False改为True

from dataclasses import dataclass

@dataclass(frozen=True)
class Student:
    name: str
    age: int


stu = Student("zhangsan", 18)
print(stu)
# Student(name='zhangsan', age=18)

然后我们修改年龄,结果发生报错

stu.age = 20

"""
Traceback (most recent call last):
  File "D:\01 programming\09python\python-tools\test\demo01\python数据类dataclass.py", line 70, in <module>
    stu.age = 20
  File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'age'
"""
3.2 数据比较

首先dataclass注解中order的值改为True,表示开启__eq__(),lt()等方法
然后修改field的compare属性的值为False,关闭对name属性的比较

from dataclasses import dataclass, field

@dataclass(order=True)
class Student:
    name: str = field(compare=False)
    age: int


stus = [Student('zhangsan', 20), Student('lisi', 21), Student('wangwu', 19)]
# 根据学生年龄排序
print(sorted(stus))
# [Student(name='wangwu', age=19), Student(name='zhangsan', age=20), Student(name='lisi', age=21)]
3.3 后处理

通过__post_init__可做后处理(在__init__返回前,自动调用)
首先将field中init属性改为False,表示该字段属性不进行初始化的操作
然后在__post_init__中,对定义的info属性赋值,进行初始化的后处理

from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    age: int = 0
    # 个人信息
    info: str = field(init=False)

    def __post_init__(self):
        self.info = f'我的名字叫{self.name}, 今年{self.age}岁了'

stu = Student("张三", 20)
print(stu)
# Student(name='张三', age=20, info='我的名字叫张三, 今年20岁了')

💖 欢迎我的关注公众号

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值