You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Python类本身访问类属性名以提升可读性与类型提示?

获取Python类属性名称的非字符串硬编码方案

需求背景

假设有以下类定义:

class MyClass:
    attribute1: str
    attribute2: int


class MySecondClass:
    another_attribute1: str
    another_attribute2: int

想直接通过类拿到属性名的字符串(比如MyClass.attribute1.fieldname返回'attribute1'),避免硬编码字符串带来的重构失效、无类型提示等问题,下面是两个典型场景:

场景1:跨实例字段更新

要写个方法用MySecondClass实例更新MyClass实例,返回布尔值表示是否完成更新。原本用字典存字段映射循环处理的代码如下:

my_class_instance: MyClass
my_second_class_instance: MySecondClass

field_map = {
    'attribute1': 'another_attribute1',
    'attribute2': 'another_attribute2'
}

updated = False
for field1, field2 in field_map.items():
    current_value = getattr(my_class_instance, field1)
    new_value = getattr(my_second_class_instance, field2)

    if current_value != new_value:
        setattr(my_class_instance, field1, new_value)
        updated = True

return updated

问题:吃不到IDE的类型提示和自动重构福利。要是把MyClass的attribute1重命名,field_map里的字符串不会自动跟着变,直接报错。逐个字段判断的话,字段多了代码又会臃肿不堪。

场景2:Django/DRF中的硬编码坑

Django模型定义:

class MyModel(models.Model):
    field1 = models.FloatField()
    field2 = models.IntegerField()

对应的DRF序列化器:

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ['field1', 'field2']

问题:fields是字符串数组,要是把field1改名叫my_client_changed_the_requirements,序列化器因为fields没更新直接失效。要是能写成fields = [MyModel.field1.fieldname, MyModel.field2.fieldname],好处很明显:

  • 重命名字段时IDE自动同步更新fields数组
  • 编写时IDE能直接提示所有可用字段

Django ORM的过滤操作也有同样的硬编码问题。

现有实现方案

1. 普通类:自定义描述器

对于只定义类型注解的类,可以用自定义描述器给每个属性绑定名称:

class FieldName:
    def __set_name__(self, owner, name):
        self.fieldname = name
        # 把当前实例绑定到类的对应属性上
        setattr(owner, name, self)

# 修改类定义
class MyClass:
    attribute1 = FieldName()
    attribute2 = FieldName()

class MySecondClass:
    another_attribute1 = FieldName()
    another_attribute2 = FieldName()

这样就能通过MyClass.attribute1.fieldname拿到'attribute1',字段映射可以写成:

field_map = {
    MyClass.attribute1.fieldname: MySecondClass.another_attribute1.fieldname,
    MyClass.attribute2.fieldname: MySecondClass.another_attribute2.fieldname
}

要保留类型注解的话,可以扩展描述器:

class TypedFieldName(FieldName):
    def __init__(self, type_):
        self.type = type_

class MyClass:
    attribute1 = TypedFieldName(str)
    attribute2 = TypedFieldName(int)

2. Django专属:直接用字段的name属性

Django模型字段本身就自带name属性,直接用就行:

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = [MyModel.field1.name, MyModel.field2.name]

ORM过滤也可以这么写:

MyModel.objects.filter(**{MyModel.field1.name: 10.0})

批量获取字段名的话,还能通过MyModel._meta.get_fields()遍历拿到所有字段的name。

3. 通用方案:用inspect模块批量获取

对于任意类,用inspect模块可以批量获取属性名,适合批量处理场景:

import inspect

def get_field_names(cls):
    # 过滤掉方法,只保留属性
    return [name for name, member in inspect.getmembers(cls) 
            if not inspect.isfunction(member) and not inspect.ismethod(member)]

给Python加原生支持的可能性

Python目前没有原生语法支持直接通过类属性获取其名称,要加的话得提PEP提案。但考虑到Python的动态特性,原生支持需要兼顾兼容性和语义一致性,短期内落地概率不高。更实际的是用第三方库,比如pydantic的模型可以通过__fields__拿到字段名:

from pydantic import BaseModel

class MyClass(BaseModel):
    attribute1: str
    attribute2: int

# 获取单个字段名
MyClass.__fields__['attribute1'].name  # 返回'attribute1'

内容的提问来源于stack exchange,提问作者Job

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:02:46