@dataclass的作用

前言

最近在阅读代码时学习到了一种新的用法,在定义类的时候会在上方加入类似@dataclass和@classmethod的用法,因此记录梳理下这种用法的含义。

一、 @dataclass

@dataclass的主要作用是可以简化数据类的定义,相比传统定义,可以自动生成__init__, __repr__, __eq__等方法。

举个例子来说,我们定义一个"学生"类,需要存储姓名、年龄、成绩列表,并能方便打印和比较。传统方法如下:

复制代码
class Student:
    def __init__(self, name, age, scores=None):
        self.name = name
        self.age = age
        # 防止所有实例共享同一个列表(经典陷阱)
        if scores is None:
            self.scores = []
        else:
            self.scores = scores[:]

    def __repr__(self):
        return f"Student(name='{self.name}', age={self.age}, scores={self.scores})"

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return (self.name == other.name and
                self.age == other.age and
                self.scores == other.scores)

    def average_score(self):
        if not self.scores:
            return 0.0
        return sum(self.scores) / len(self.scores)


# 使用
s1 = Student("小明", 18, [85, 92, 78])
s2 = Student("小明", 18, [85, 92, 78])

print(s1)                    # Student(name='小明', age=18, scores=[85, 92, 78])
print(s1 == s2)              # True
print(s1.average_score())    # 85.0

但是,如果使用@dataclass就可以将代码进行简化

复制代码
from dataclasses import dataclass, field
from typing import List

@dataclass
class Student:
    name: str
    age: int
    scores: List[int] = field(default_factory=list)  # 安全默认值

    def average_score(self):
        if not self.scores:
            return 0.0
        return sum(self.scores) / len(self.scores)


# 使用完全一样
s1 = Student("小明", 18, [85, 92, 78])
s2 = Student("小明", 18, [85, 92, 78])

print(s1)                    # Student(name='小明', age=18, scores=[85, 92, 78])
print(s1 == s2)              # True(自动实现)
print(s1.average_score())    # 85.0

总结:使用@dataclass之后,在定义class时,再也不用写初始化函数__init__,相等比较函数__eq__了。

二、@classmethod

@classmethod 是内置的装饰器(decorator),位于 types 模块中,但通常直接用作类方法装饰器。它用于将一个普通函数转换为类方法(class method)。类方法是绑定到类本身(而非实例)的特殊方法,可以通过类名或实例调用,主要用于操作类级别的状态或创建替代构造器。

基本用法

复制代码
class MyClass:
    @classmethod
    def my_method(cls, arg1, arg2):
        # cls 是类本身
        pass
  • @classmethod 必须写在函数定义上方。
  • 函数的第一个参数必须是 cls(约定俗成,代表 class),它自动接收调用时的类(可能是子类)。
  • 调用方式:
    通过类:MyClass.my_method(...)
    通过实例:instance = MyClass(); instance.my_method(...)(但内部还是用类调用)。

举例子来说

复制代码
class Person:
    @classmethod
    def from_string(cls, string):
        name, age = string.split(',')
        return cls(name.strip(), int(age.strip()))

    def __init__(self, name, age):
        self.name = name
        self.age = age


# 使用方式(非常自然、直观)
p = Person.from_string("Alice, 30")   # ← 直接通过类名调用,像"工厂"一样创建对象
print(p.name, p.age)                  # Alice 30

如果不想使用@classmethod,代码就会变成

复制代码
class Person:
    def from_string(self, string):           # 注意:第一个参数变成了 self
        name, age = string.split(',')
        return Person(name.strip(), int(age.strip()))

    def __init__(self, name, age):
        self.name = name
        self.age = age
p=Person("Alice", 30)

总结,使用@classmethod并不会减少代码量,它能做的就是可以很方便的迅速初始化实例类,特别是通过class内部定义的函数对类进行初始化。例如在上面例子中通过from_string对类进行初始化实例。

相关推荐
apcipot_rain7 小时前
C++转Java日记(04)
java·开发语言·c++
特立独行的猫A8 小时前
我给女儿写了个打字练习:一边敲键盘,一边把小学英语单词记牢
java·后端·python
cpolar技术支持8 小时前
「我这边连不上你那个服务」—— 用清单驱动的 TCP 连通性只读看板,cpolar 让远端同事自己取证
python·内网穿透·tcp·cpolar·网络诊断
云和数据.ChenGuang8 小时前
LangGraph 异步 Node 的设计与写法实践
开发语言·人工智能·python·机器学习·php
天诰9 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
benchmark_cc10 小时前
用 QuantDash 读取美股行情:AAPL.US 的运行前检查、排错顺序与数据校验清单
python·数据分析·pandas·量化交易·股票数据·quantdash
蜗牛一步步往上爬12 小时前
第十六章 C++语言在深度学习中的应用
开发语言·c++·深度学习
艺杯羹12 小时前
Windows原生软件锁设计:从暴力杀进程丢数据到零轮询事件驱动架构
windows·系统架构·开源·开源软件·内存管理·开源协议
羲云网络14 小时前
GPT Image 2.6 接入指南:Flare、Sunburst 怎么选,编辑失败如何排查
人工智能·python·aigc·图像生成·acedatacloud
张张123y14 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent