使用dataclass 和 namedtuple动态定义数据结构
前言
如果经常用 java 或者 go 写业务,哪怕是非常简单的业务,比如简单的 crud,我们都需要先去定义一个数据结构,然会会用这个数据结构去映射一张表,在 java 里,通过 class 和 数据库的表做映射,go 中是使用 struct 结构体。甚至有的规范还需要去定义 pojo,bo、dto、vo 等等。
定义这些数据结构非常的繁琐并且机械,所以现在有非常好用的代码生成工具, 能够帮助我们通过表结构来生成对应的数据结构。唯一麻烦的是,这是一个静态的操作,需要手动去组织生成的代码。因为 java 和 go 都需要编译,所以没办法在运行时来做这些事情。
虽然 python 的性能不如 java 和 go,但是 python 的灵活性是 java 和 go 完全不能比的, 就上面说的这个场景,python 就有非常好的特性在运行时动态生成数据结构 —— namedtuple和dataclass
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])