MelonBlog

使用dataclass 和 namedtuple动态定义数据结构

前言

如果经常用 java 或者 go 写业务,哪怕是非常简单的业务,比如简单的 crud,我们都需要先去定义一个数据结构,然会会用这个数据结构去映射一张表,在 java 里,通过 class 和 数据库的表做映射,go 中是使用 struct 结构体。甚至有的规范还需要去定义 pojo,bo、dto、vo 等等。

定义这些数据结构非常的繁琐并且机械,所以现在有非常好用的代码生成工具, 能够帮助我们通过表结构来生成对应的数据结构。唯一麻烦的是,这是一个静态的操作,需要手动去组织生成的代码。因为 java 和 go 都需要编译,所以没办法在运行时来做这些事情。

虽然 python 的性能不如 java 和 go,但是 python 的灵活性是 java 和 go 完全不能比的, 就上面说的这个场景,python 就有非常好的特性在运行时动态生成数据结构 —— namedtupledataclass

namedtuple

namedtuple本质上是一个元组,元组需要通过索引来获取元素,如果元组的维度比较多的时候,很难去记住,每一个索引代表的含义。namedtuple 可以给每一个索引取一个有意义的名字, 然后像使用对象一样使用元组。

namedtuple 和 tuple 一样,是不可变的,所以常规的修改 tuple 里面的值会报错,如果想修改 namedtuple的值,可以通过_replace 方法

例子🌰

from collections import namedtuple
# 定义一个叫 Person 的结构,包含name 和 age 两个属性
Person = namedtuple('Person', ['name', 'age'])
# 创建一个Person实例
john = Person(name='John', age=30)
# 访问 namedtuple 的属性
print(john.name)  # Output: John
print(john.age)   # Output: 30
# john.age = 18
# print(john.age)
# Output: AttributeError: can't set attribute
# 由于 namedtuple 的属性是只读的,所以不能修改属性的值
# 但是可以通过 _replace 方法创建一个新的 namedtuple 实例,来修改属性的值
john = john._replace(age=18)
print(john.age)  # Output: 18

dataclass

dataclass 是一个装饰器,使用这个装饰器可以更简单地定义数据结构,并且dataclasses包还提供了一个动态创建 dataclass 的函数——make_dataclass

和 namedtuple 不同的是,dataclass 对象可以自由的修改属性,它比 namedtuple 更像 java 中的 pojo 对象

例子🌰

from dataclasses import make_dataclass, dataclass
@dataclass
class Person:
    name: str
    age: int
john = Person('John', 30)
print(john.name)  # Output: John
print(john.age)  # Output: 30
john.age = '18'
print(john.age)  # Output: 18
print(type(john.age))  # Output: <class 'str'>
Person = make_dataclass('Person', [('name', str), ('age', int)])
john = Person('John', 30)
print(john.name)  # Output: John
print(john.age)  # Output: 30
print(type(john.age))  # Output: <class 'int'>
john.age = '18'
print(john.age)  # Output: 18
print(type(john.age))  # Output: <class 'str'>

动态定义pojo

既然namedtuple和 dataclass 都可以动态的定义数据结构,那普通的 pojo 完全就可以在运行时自动生成


mysql 提供了 show columns 语句来查询某一张表的数据结构

show columns from {table}

例子🌰

show columns from user
+---------+-------------------+----+---+-----------------+---------------------------+
|Field    |Type               |Null|Key|Default          |Extra                      |
+---------+-------------------+----+---+-----------------+---------------------------+
|id       |bigint(20) unsigned|NO  |PRI|null             |auto_increment             |
|username |varchar(32)        |NO  |   |null             |                           |
|password |varchar(32)        |NO  |   |null             |                           |
|nickname |varchar(32)        |NO  |   |null             |                           |
|avatar   |varchar(255)       |YES |   |null             |                           |
|status   |tinyint(1)         |NO  |   |1                |                           |
|create_by|bigint(20) unsigned|YES |   |null             |                           |
|update_by|bigint(20) unsigned|YES |   |null             |                           |
|create_at|datetime           |NO  |   |CURRENT_TIMESTAMP|                           |
|update_at|datetime           |NO  |   |CURRENT_TIMESTAMP|on update CURRENT_TIMESTAMP|
|deleted  |tinyint(1)         |NO  |   |0                |                           |
+---------+-------------------+----+---+-----------------+---------------------------+


通过表结构构建动态数据结构

# columns: 字段列表
# 通过 namedtuple 创建 User 实体
User = namedtuple("User", [col[0] for col in columns])
# 通过dataclass 创建User实体
User = make_dataclass('Person', [col[0] for col in columns])