当前位置: 首页 > article >正文

python:数据类构建器

在 Python 中,​数据类(Data Classes)​ 用于快速创建主要目的是存储数据的类,自动生成 __init____repr____eq__ 等方法。

“Python 提供了几种构建简单类的方式,这些类只是字段的容器,几乎没
有额外功能。这种模式称为数据类data class),dataclasses 包就
支持该模式。”

 引用自《流畅的python(第二版)》


1. 基础数据类

使用 @dataclass 装饰器

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
    email: str = ""  # 默认值

自动生成的方法:​

  • __init__(self, name: str, age: int, email: str = "")
  • __repr__(): 返回可读的字符串表示,如
  •  Person(name='Alice', age=30, email='alice@example.com')
  • __eq__(): 比较所有字段是否相等

@dataclass 是 Python 中用于快速生成数据类的装饰器,支持多个关键字参数以定制类的行为。

 1. 核心参数列表

参数

作用

默认值

备注

init

是否自动生成 __init__ 方法

True

若禁用(init=False),需手动定义初始化逻辑。

repr

是否生成 __repr__ 方法(可读的字符串表示)

True

禁用后,打印对象时显示默认类名和内存地址。

eq

是否生成 __eq__ 方法(比较所有字段是否相等)

True

禁用后,对象比较基于实例内存地址(is 语义)。

order

是否生成比较方法(<<=>>=

False

需显式启用(order=True),且依赖 eq=True

unsafe_hash

是否生成 __hash__ 方法

False

仅在字段不可变且 eq 和 frozen 为 True 时安全使用。

frozen

是否使实例不可变(字段不可修改)

False

启用后,修改字段会抛出 FrozenInstanceError


2. 参数详解与示例

(1) init=True

  • 作用:自动生成 __init__ 方法,允许通过构造函数初始化字段。

  • 禁用场景:需自定义初始化逻辑(如字段验证)。

@dataclass(init=False)
class Person:
    name: str
    age: int

    def __init__(self, name: str, birth_year: int):
        self.name = name
        self.age = 2023 - birth_year  # 自定义初始化逻辑

​(2) order=True

  • 作用:生成比较方法(按字段声明顺序逐值比较)。

  • 依赖:需同时启用 eq=True(默认已启用)。

@dataclass(order=True)
class Student:
    name: str
    score: int

s1 = Student("Alice", 90)
s2 = Student("Bob", 85)
print(s1 > s2)  # 输出: True(先比较 name,若相同则比较 score)

(3) frozen=True     

  • 作用:创建不可变实例(类似 namedtuple)。

  • 修改方式:通过 dataclasses.replace() 生成新实例。        

@dataclass(frozen=True)
class Point:
    x: int
    y: int

p = Point(1, 2)
p_new = replace(p, x=3)  # 创建新实例

(4) unsafe_hash=False

  • 作用:决定是否生成 __hash__ 方法。

  • 安全条件:仅当 frozen=True 且 eq=True 时,哈希值才可靠。

@dataclass(frozen=True, unsafe_hash=True)
class User:
    id: int
    name: str

users = {User(1, "Alice")}  # 允许将实例加入集合或作为字典键

3. 参数组合使用场景

场景

推荐参数组合

说明

不可变配置类

frozen=True, eq=True

确保数据不可变且可哈希

排序支持

order=True, eq=True

需启用 eq 以支持比较操作

自定义初始化逻辑

init=False

手动定义 __init__ 方法

仅数据容器(无方法)

init=True, repr=False, eq=False

禁用冗余方法以减少内存占用


4. 完整示例

from dataclasses import dataclass, replace

@dataclass(init=True, repr=True, eq=True, order=False, frozen=False)
class Product:
    id: int
    name: str
    price: float = 0.0  # 默认值

# 使用自动生成的 __init__ 和 __repr__
p1 = Product(1, "Laptop", 999.99)
print(p1)  # 输出: Product(id=1, name='Laptop', price=999.99)

# 修改字段(需 frozen=False)
p1.price = 899.99

总结

  • 核心作用:通过 @dataclass 快速生成数据类,减少样板代码。

  • 参数选择:根据需求启用或禁用特定方法(如不可变性、排序)。

  • 注意事项

    • order=True 依赖字段声明顺序。

    • unsafe_hash=True 仅在特定条件下安全使用。

    • frozen=True 适用于线程安全或哈希键场景。

通过 namedtuple 可以定义一个包含字段名的类

from collections import namedtuple

# 定义一个具名元组类
Person = namedtuple("Person", ["name", "age", "email"])
  • 参数说明
    • 第一个参数:类名(字符串)。
    • 第二个参数:字段名列表(字符串的可迭代对象,如列表或空格分隔的字符串 "name age email")。

 一个用 namedtuple 的例子,请看一下代码

from collections import namedtuple
import json

# 定义嵌套的具名元组
Coordinate = namedtuple("Coordinate", ["lat", "lon"])

# 定义 City 类
City = namedtuple("City", "name country population location")

# 原始数据
delhi_data = ("Delhi NCR", "IN", 21.935, Coordinate(28.613889, 77.208889))

# 创建实例
delhi = City._make(delhi_data)

# 转换为字典并序列化为 JSON
delhi_dict = delhi._asdict()
delhi_dict["location"] = list(delhi.location)  # 将 Coordinate 转为列表
json_str = json.dumps(delhi_dict, indent=2)
print(json_str)

1. 定义具名元组 City

from collections import namedtuple

# 定义具名元组 City,包含字段:name, country, population, location
City = namedtuple("City", "name country population location")
  • 作用:创建一个不可变的类 City,其实例可以通过字段名访问属性(如 city.name),同时保留元组的特性(如索引访问 city[0])。

  • 优势:比普通类更简洁,比字典更内存高效,适合表示简单的数据记录。


2. 关键属性和方法解析

(1) _fields 属性

print(City._fields)  # 输出:('name', 'country', 'population', 'location')
  • 功能:返回一个元组,包含类中所有字段的名称。

  • 用途:动态获取字段名,适用于需要反射或批量操作的场景。


​(2) _make() 类方法

# 定义嵌套的具名元组 Coordinate
Coordinate = namedtuple("Coordinate", ["lat", "lon"])

# 原始数据(假设从数据库或 CSV 读取)
delhi_data = ("Delhi NCR", "IN", 21.935, Coordinate(28.613889, 77.208889))

# 通过 _make() 创建 City 实例
delhi = City._make(delhi_data)
  • 功能:从可迭代对象(如列表、元组)直接构建实例,等价于 City(*delhi_data)

  • 适用场景:处理数据库查询结果、CSV 文件行数据等批量创建实例的场景。


​(3) _asdict() 实例方法

import json

# 将实例转换为字典
delhi_dict = delhi._asdict()

# 序列化为 JSON
json_str = json.dumps(delhi_dict, indent=2)
print(json_str)
  • 输出

    {
      "name": "Delhi NCR",
      "country": "IN",
      "population": 21.935,
      "location": [28.613889, 77.208889]
    }
  • 功能:将具名元组实例转换为标准字典,支持嵌套对象的序列化(如 Coordinate 被转换为列表)。

  • 用途:方便与 JSON、YAML 等数据格式交互,或传递给需要字典的 API。


3. 关键注意事项

  • 不可变性:具名元组实例的字段值不可修改。若需更新值,需通过 _replace() 方法生成新实例:

    delhi_updated = delhi._replace(population=22.0)

4. 对比 _make() 与直接实例化

  • 等价性

    # 两种方式结果相同
    delhi1 = City._make(delhi_data)
    delhi2 = City(*delhi_data)
  • 代码可读性_make() 更清晰地表达“从数据行构建实例”的意图。


5. 适用场景总结

  • 替代字典:当数据字段固定且需通过名称访问时(如配置项、数据库记录)。

  • 替代普通类:当类仅用于存储数据且无需复杂方法时。

  • 高性能场景处理大规模数据时,内存占用低于普通类。

 为具名元组添加方法

核心目标: ​在 namedtuple 生成的 Card 类上添加排序方法,实现扑克牌的排序功能


1. 正确定义 Card 具名元组

from collections import namedtuple

# 定义 Card 类,包含 rank(牌面)和 suit(花色)字段
Card = namedtuple('Card', ['rank', 'suit'])

2. 添加类属性 suit_values

为 Card 类添加花色对应的权重值(如黑桃最高):

# 类属性:各花色的权重(黑桃 > 红心 > 方块 > 梅花)
Card.suit_values = {'spades': 3, 'hearts': 2, 'diamonds': 1, 'clubs': 0}

3. 定义排序逻辑函数

编写计算单张牌总权重的函数 spades_high

# 定义扑克牌的排序规则(按牌面升序,花色权重降序)
ranks = [str(n) for n in range(2, 11)] + ['J', 'Q', 'K', 'A']  # 牌面顺序:2~10, J, Q, K, A

def spades_high(card):
    # 获取牌面在 ranks 中的索引(0~12)
    rank_value = ranks.index(card.rank)
    # 获取花色的权重值
    suit_value = Card.suit_values[card.suit]
    # 计算总权重:牌面优先级 * 花色数量 + 花色权重
    return rank_value * len(Card.suit_values) + suit_value

4. 将函数绑定为 Card 的方法

将 spades_high 函数作为方法添加到 Card 类,并重命名为 overall_rank

# 将函数绑定为 Card 的实例方法
Card.overall_rank = spades_high

5. 测试验证

创建测试用例,验证最低牌和最高牌的权重:

# 创建测试卡牌
lowest_card = Card('2', 'clubs')    # 梅花2(权重最低)
highest_card = Card('A', 'spades')  # 黑桃A(权重最高)

# 调用方法验证结果
print(lowest_card.overall_rank())   # 输出: 0
print(highest_card.overall_rank())  # 输出: 51

完整代码

from collections import namedtuple

# 1. 定义 Card 具名元组
Card = namedtuple('Card', ['rank', 'suit'])

# 2. 添加类属性:花色权重
Card.suit_values = {'spades': 3, 'hearts': 2, 'diamonds': 1, 'clubs': 0}

# 3. 定义牌面顺序
ranks = [str(n) for n in range(2, 11)] + ['J', 'Q', 'K', 'A']

# 4. 定义排序逻辑函数
def spades_high(card):
    rank_value = ranks.index(card.rank)
    suit_value = Card.suit_values[card.suit]
    return rank_value * len(Card.suit_values) + suit_value

# 5. 将函数绑定为 Card 的方法
Card.overall_rank = spades_high

# 6. 测试验证
if __name__ == '__main__':
    lowest_card = Card('2', 'clubs')
    highest_card = Card('A', 'spades')
    print(f"最低牌权重: {lowest_card.overall_rank()}")   # 输出: 0
    print(f"最高牌权重: {highest_card.overall_rank()}")   # 输出: 51

核心逻辑验证

  • 最低牌(梅花2)​
    rank_value = 0ranks.index('2')),suit_value = 0,总权重 = 0 * 4 + 0 = 0

  • 最高牌(黑桃A)​
    rank_value = 12ranks.index('A')),suit_value = 3,总权重 = 12 * 4 + 3 = 51

通过此方法,可在不修改 namedtuple 底层实现的情况下,灵活扩展功能,适用于需要快速定义轻量级数据类并添加简单方法的场景。


http://www.kler.cn/a/586703.html

相关文章:

  • 《DeepSeek深度使用教程:开启智能交互新体验》Deepseek深度使用教程
  • 【大模型基础_毛玉仁】2.4 基于 Encoder-Decoder 架构的大语言模型
  • AtCoder Beginner Contest 003(A - 社の給料、B -トランプ、C -プログラミング講座、D - 社の冬 )题目讲解
  • 【PHP】新版本特性记录(持续更新)
  • java 的标记接口RandomAccess使用方法
  • vulnhub靶场之stapler靶机
  • MIDI,AI 3D场景生成技术
  • Audacity 技术浅析(一)
  • [CISSP] [3] 人员安全与社会工程
  • 原生微信小程序实现导航漫游(Tour)
  • 农作物病害数据集
  • 性能优化:javascript 如何检测并处理页面卡顿
  • A SURVEY ON POST-TRAINING OF LARGE LANGUAGE MODELS——大型语言模型的训练后优化综述——第2部分
  • 模型评估指标详解:分类与回归场景
  • 基于SpringBoot+Vue的毕业论文管理系统+LW示例参考
  • 【NLP】 5. Word Analogy Task(词类比任务)与 Intrinsic Metric(内在度量)
  • 微信小程序面试内容整理-JSON
  • 【c++】【线程】【信号量】三个线程顺序打印1--100
  • docker pull 镜像问题
  • 【Rust交叉编译】在x86_64架构下交叉编译aarch64-linux-musl版的rust-opencv