Hexo

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

Python——field的用法


整体说明

  • 在 Python 中,field 主要关联两个核心场景:
    • 一是标准库 dataclasses 模块的 field() 函数 ,用于定制数据类字段
    • 二是第三方库如 pydantic 的 Field 类 (注意:首字母是大写), 用于数据校验/序列化

dataclasses.field()

  • dataclasses 是 Python 内置的轻量级数据类工具
  • field() 用于精细化定义数据类的字段(替代默认的简单赋值),支持定制默认值、初始化行为、序列化等
    • 注:Python 3.7+ 内置

dataclasses.field() 基础语法

  • 用法示例:

    1
    2
    3
    4
    5
    from dataclasses import dataclass, field

    @dataclass
    class ClassName:
    name = field(...) # 字段名: 类型 = Field(参数1=值1, 参数2=值2, ...)
  • field() 核心参数说明

    • default
      • 字段默认值(仅当字段无默认值时使用,与 default_factory 二选一)
      • 示例:field(default=0)
    • default_factory
      • 动态生成默认值的工厂函数(如列表/字典等可变类型)
      • 示例:field(default_factory=list)
    • init
      • 是否参与 __init__ 方法(默认 True)
      • 示例:field(init=False)
    • repr
      • 是否出现在 __repr__ 输出中(默认 True)
    • compare
      • 是否参与比较(__eq__/__lt__ 等,默认 True)
    • hash
      • 是否参与 __hash__ 计算(默认 None,继承 compare 值)
    • metadata
      • 附加元数据(字典,供外部工具使用)
      • field(metadata={"desc": "用户ID"})

dataclasses.field() 常用示例

  • 示例:基础使用(默认值/工厂函数)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from dataclasses import dataclass, field

    @dataclass
    class User:
    # 简单默认值(不可变类型)
    id: int = field(default=0)
    # 可变类型默认值(必须用 default_factory,避免所有实例共享同一对象)
    tags: list[str] = field(default_factory=list)
    # 字符串默认值
    name: str = field(default="未知用户")

    # 实例化
    u1 = User()
    print(u1) # User(id=0, tags=[], name='未知用户')
    u1.tags.append("admin")
    u2 = User()
    print(u2.tags) # [](独立的列表,无共享问题)
  • 示例2:定制初始化/序列化行为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @dataclass
    class Product:
    name: str
    # 不参与 __init__(手动赋值)
    price: float = field(init=False)
    # 不显示在 repr 中
    stock: int = field(default=0, repr=False)
    # 不参与比较
    sku: str = field(default="", compare=False)

    # 实例化(无需传 price 和 stock/sku)
    p = Product("手机")
    p.price = 2999.99 # 手动赋值
    print(p) # Product(name='手机', price=2999.99)(stock 未显示)
    print(p == Product("手机")) # True(sku 不参与比较)
  • 示例3:附加元数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @dataclass
    class Student:
    id: int = field(metadata={"desc": "学生学号", "required": True})
    score: float = field(default=0.0, metadata={"min": 0, "max": 100})

    # 获取元数据
    s = Student(1001)
    # 方式1:通过 dataclasses.fields 获取
    from dataclasses import fields
    for f in fields(s):
    print(f.name, f.metadata)
    # 输出:
    # id {'desc': '学生学号', 'required': True}
    # score {'min': 0, 'max': 100}

pydantic.Field(第三方库,数据校验)

  • pydantic 是Python主流的数据校验库
  • Field 用于定义模型字段的校验规则、默认值、文档等,功能比 dataclasses.field 更丰富
  • 使用前先安装:
    1
    pip install pydantic

pydantic.Field 基础语法

  • 用法说明:

    1
    2
    3
    4
    from pydantic import BaseModel, Field

    class ClassName(BaseModel):
    name = field(...) # 字段名: 类型 = Field(默认值, 参数1=值1, 参数2=值2, ...)
  • 核心参数说明

    • default/default_factory:
      • 默认值/动态默认值(同dataclasses)
      • 示例:Field(default=10) / Field(default_factory=list)
    • alias
      • 字段别名(序列化/反序列化时可用)
      • 示例: Field(alias="user_id")
    • gt/ge/ lt/le
      • 数值大于/大于等于/小于/小于等于
      • 示例:Field(gt=0)(值必须>0)
    • min_length/max_length
      • 字符串最小/最大长度
      • 示例:Field(min_length=2, max_length=10)
    • pattern
      • 字符串正则匹配
      • 示例:Field(pattern=r"^[A-Z]+$")
    • description
      • 字段描述(文档生成)
      • 示例:Field(description="用户年龄")
    • nullable
      • 是否允许为None(Pydantic v1,v2需用 Optional)
      • 示例:Field(nullable=True)
    • examples
      • 示例值(OpenAPI文档)
      • 示例:Field(examples=[18, 20])

pydantic.Field常用示例

  • 示例1:基础校验

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    from pydantic import BaseModel, Field

    class User(BaseModel):
    name: str = Field(..., min_length=2, max_length=20, description="用户名(2-20字符)")
    age: int = Field(..., gt=0, le=120, description="年龄(1-120)")
    email: str = Field(None, pattern=r"^[\w-]+@[\w-]+\.[a-z]+$", description="邮箱(可选)")

    # 合法实例
    u1 = User(name="张三", age=25, email="zhangsan@example.com")
    print(u1.model_dump())
    # 输出:{'name': '张三', 'age': 25, 'email': 'zhangsan@example.com'}

    # 非法实例(触发校验错误)
    try:
    u2 = User(name="李", age=150, email="invalid-email")
    except Exception as e:
    print(e)
    # 输出:
    # 1 validation error for User
    # name
    # String should have at least 2 characters [type=string_too_short, input_value='李', input_type=str]
    # age
    # Input should be less than or equal to 120 [type=less_than_or_equal, input_value=150, input_type=int]
    # email
    # String should match pattern '^[\w-]+@[\w-]+\.[a-z]+$' [type=string_pattern_mismatch, input_value='invalid-email', input_type=str]
  • 示例2:别名与默认值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Product(BaseModel):
    # 别名:序列化时用 product_id,反序列化时可传 id 或 product_id
    id: int = Field(..., alias="product_id")
    # 动态默认值(每次实例化生成新列表)
    tags: list[str] = Field(default_factory=lambda: ["未分类"])

    # 用别名传参
    p = Product(product_id=1001)
    print(p.id) # 1001
    print(p.tags) # ['未分类']
    # 序列化(输出别名)
    print(p.model_dump(by_alias=True)) # {'product_id': 1001, 'tags': ['未分类']}
  • 示例3:结合文档(OpenAPI)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from pydantic import BaseModel, Field
    from pydantic.schema import schema

    class Order(BaseModel):
    order_id: str = Field(..., pattern=r"^ORD-\d{6}$", description="订单号(格式:ORD-6位数字)")
    amount: float = Field(..., gt=0, examples=[99.9, 199.0], description="订单金额(>0)")

    # 生成JSON Schema(用于OpenAPI文档)
    schema_dict = schema([Order])
    print(schema_dict)

pydantic.Field 和 dataclasses.field 区别

  • dataclasses.field:
    • 内置库
    • 简单数据存储、无校验需求
    • 无校验能力(仅基础类型注解)
  • pydantic.Field:
    • 第三方接口
    • 接口参数校验、数据清洗、API文档
    • 强大的数值/字符串/结构校验

使用注意

  • 可变类型默认值 :
    • 无论是 dataclasses.field 还是 pydantic.Field,可变类型(list/dict/set)的默认值必须用 default_factory,否则所有实例会共享同一对象
    • 错误:tags: list = []
    • 正确:tags: list = field(default_factory=list)
  • Pydantic版本差异 :
    • v1 中 nullable=True 允许字段为None;v2 需用 Optional[类型](如 age: Optional[int] = Field(None))
    • v2 中 Field 的参数更简洁,推荐使用最新版
  • dataclasses 不可变字段 :
    • 若需不可变数据类,加 @dataclass(frozen=True),此时 init=False 的字段需在 __post_init__ 中赋值

Python——easydict包的使用


整体说明

  • EasyDict 是一个轻量级的 Python 库,旨在简化字典操作,它允许用户像访问对象属性一样访问字典的键值对,从而提高代码的可读性和简洁性
  • EasyDict 通过重写字典的几个关键方法,如__getattr__和__setattr__等,实现了将字典键转换为对象属性的功能
  • EasyDict 不仅支持顶级字典的属性访问方式,还能递归应用于内嵌的字典,使得处理多层次数据结构变得简单易行
  • EasyDict 实例仍然遵循标准字典的所有操作,保证了灵活性

安装 EasyDict

  • 可以使用pip进行安装,命令如下:
    1
    pip install easydict

使用示例

  • 简单使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from easydict import EasyDict as edict
    # 创建一个EasyDict对象
    data = edict({'name': 'John', 'age': 30, 'job': 'Engineer'})
    # 访问字典元素
    print(data.age)
    print(data.job)
    # 添加新的键值对
    data.gender = 'Male'
    print(data.gender)
  • 嵌套字典的访问:

    1
    2
    3
    4
    5
    6
    7
    8
    my_dict = edict({
    'level1': edict({
    'level2': edict({
    'key': 'value'
    })
    })
    })
    print(my_dict.level1.level2.key)
  • 动态设置属性:

    1
    2
    3
    my_dict = edict()
    my_dict.key1 = 'value1'
    print(my_dict.key1)
  • 常见的字典操作:

    1
    2
    3
    4
    5
    6
    7
    my_dict = edict({'key1': 'value1'})
    # 更新字典
    my_dict.update({'key2': 'value2'})
    print(my_dict.key2)
    # 删除一个键值对
    my_dict.pop('key1')
    print(my_dict.key1) # 会抛出AttributeError,因为'key1'不再存在
  • 获取默认值:

    1
    2
    3
    my_dict = edict({'name': 'Alice'})
    value = my_dict.get('nonexistent_key', 'default_value')
    print(value)

EasyDict 和 namedtuple 对比

  • EasyDict 和 namedtuple 都是 Python 中用于简化数据访问的工具
  • TDRL:namedtuple 是”先定义类,再用类创建实例”;EasyDict 是”直接用通用类创建实例,动态定义结构”
    • namedtuple 需要先定义特定结构的类(如Person),再创建该类的实例,适合固定结构的数据
    • EasyDict 直接使用通用的EasyDict类创建实例,实例的字段结构可以动态变化,适合灵活的数据场景
  • TDRL:若需 固定结构、不可变数据 ,追求性能和内存效率,用 namedtuple;若需 动态结构、灵活修改 ,优先便捷性,用 EasyDict

本质与继承关系

  • namedtuple 是 tuple 的子类,属于不可变(immutable)数据结构
    • 一旦创建,其字段值无法修改,类似元组的特性
  • namedtuple 定义时需要指定固定的字段名,结构是静态的,不能动态添加新字段
  • EasyDict 是 dict 的子类,属于可变(mutable)数据结构
    • 创建后可以随时修改字段值,也能动态添加/删除新字段,保留了字典的灵活性

数据访问方式

  • 两者都支持 属性式访问(如 obj.field)和 键值访问(如 obj['field']),但底层实现不同:
    • namedtuple 本质是元组,字段值存储在固定位置,访问速度更快
    • EasyDict 本质是字典,通过重写 __getattr__ 实现属性访问,性能略低于 namedtuple

可变性

  • namedtuple 不可变:创建后无法修改字段值,也不能添加新字段,类似常量集合,示例如下:

    1
    2
    3
    4
    from collections import namedtuple
    Person = namedtuple('Person', ['name', 'age'])
    p = Person('Alice', 30)
    p.age = 31 # 报错:'Person' object does not support item assignment
  • EasyDict 可变:支持修改现有字段、添加新字段、删除字段等操作,示例如下:

    1
    2
    3
    4
    5
    from easydict import EasyDict as edict
    p = edict(name='Alice', age=30)
    p.age = 31 # 允许修改
    p.gender = 'Female' # 允许添加新字段
    del p.age # 允许删除字段

定义类情况

  • namedtuple 显式定义了一个新的类(如Person),这个类继承自tuple,并且在定义时就固定了字段结构,例如:

    1
    2
    3
    4
    from collections import namedtuple
    # 这里显式创建了一个名为 Person 的类
    Person = namedtuple('Person', ['name', 'age'])
    print(type(Person)) # 输出:<class 'type'>,说明是一个类
    • 后续使用时,Person() 是创建该类的实例,每个实例都严格遵循预定义的字段结构
  • EasyDict 没有要求你显式定义新的类(如Person),但它本身是一个通用的 EasyDict 类,所有实例都属于这个类,例如:

    1
    2
    3
    4
    from easydict import EasyDict as edict
    # 直接创建 EasyDict 类的实例,无需预先定义结构
    p = edict(name='Alice', age=30)
    print(type(p)) # 输出:<class 'easydict.EasyDict'>
    • 你将 p 视为一个”动态对象”,它属于 EasyDict 类,但其字段可以灵活添加/修改,不需要提前定义特定的类(如Person)

适用场景

  • namedtuple 适合存储 固定结构、不可变的数据(如配置项、记录、坐标等),强调数据的稳定性和内存效率
    • 例如:表示点坐标 Point(x=1, y=2)、数据库查询结果等
  • EasyDict 适合处理 动态结构、需要灵活修改的数据(如嵌套配置、JSON 数据解析等),强调操作的便捷性
    • 例如:解析 API 返回的 JSON 数据(可动态添加/修改字段)、多层级的配置文件等

其他差异

  • 内存占用 :namedtuple 比 EasyDict 更轻量,内存占用更少
  • 序列化 :两者都支持序列化,但 namedtuple 可直接通过 _asdict() 转换为普通字典,EasyDict 本身就是字典,可直接序列化
  • 类型提示 :namedtuple 在定义时已明确字段,类型提示更友好;EasyDict 动态字段较多,类型提示较弱
1…349350351352
San Ye

San Ye

Stay Hungry. Stay Foolish.

704 posts
53 tags
© 2026 San Ye
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4