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

为什么str()与type()返回db.Column类型名不同?动态创建Enum为何报错?

问题:Flask SQLAlchemy 中str(db.Column)与type(db.Column)返回结果差异原因

场景复现

我正在学习Flask SQLAlchemy,按常规方式完成SQLAlchemy初始化:

db = SQLAlchemy(app=app)

随后通过类定义的方式声明数据表,代码如下:

class Table1(db.Model):
    col1 = db.Column(db.Integer)

我通过如下代码查看db.Column的类型:

str(db.Column)
# 输出: "<class 'sqlalchemy.sql.schema.Column'>"
type(db.Column)
# 输出: <class 'sqlalchemy.sql.visitors.TraversibleType'>

二者返回的类型结果为什么存在差异?


补充内容(2022年6月21日 12:17)

假设存在如下基础类定义:

class C:
    pass

我原本认为以下两段代码是等价的,仅语法形式不同:

class X(C):
    pass

以及:

X = type('X', (C,), {})

但当操作对象为Enum时,使用type动态创建类会触发错误,复现代码如下:

from enum import Enum

class X1(Enum):  # 该写法运行正常
    pass

X2 = type('X2', (Enum,), {}) # 该写法触发错误

具体报错信息为:

...
  File "/usr/lib/python3.8/enum.py", line 175, in __new__
    enum_members = {k: classdict[k] for k in classdict._member_names}
AttributeError: 'dict' object has no attribute '_member_names'

上述报错的产生原因是什么?


解答

1. str(db.Column)与type(db.Column)返回值差异原因

这是Python元类的基础特性,和SQLAlchemy本身的逻辑没有冲突:

  • 首先明确:db.Column本身是一个类,不是实例。你写col1 = db.Column(db.Integer)的时候才是在实例化这个类,生成实际的列对象。
  • str(类对象)返回的是类的可读字符串表示。SQLAlchemy对Column类的字符串输出做了定制,返回它逻辑上的模块路径sqlalchemy.sql.schema.Column,方便开发者识别。
  • type(类对象)返回的是创建这个类的元类。举个最基础的例子:你执行type(int)、type(str),返回的都是<class 'type'>,因为Python内置的int、str类都是由默认元类type创建的。SQLAlchemy为了实现SQL语法树的遍历访问能力,给所有SQL结构相关的类都实现了自定义元类TraversibleType,Column类就是由这个元类创建的,所以type(db.Column)会返回这个自定义元类。

2. 动态创建Enum子类报错的原因

你之前认为class定义类和直接调用type创建类完全等价,这个结论只在父类使用默认元类type时成立。一旦父类使用了自定义元类,这个等价关系就不成立:

  • Enum类本身使用自定义元类EnumMeta实现所有枚举逻辑。当你用class关键字定义Enum子类时,Python不会死板地调用默认type创建类,而是会自动查找所有父类用到的元类,选择优先级最高的元类(这里就是EnumMeta)来执行类创建流程。EnumMeta在创建类时会自动生成带_member_names属性的特殊命名空间,用来存储枚举成员,所以正常class定义的Enum不会报错。
  • 你直接调用原生type()创建Enum子类时,完全绕过了EnumMeta的类创建逻辑,传入的是普通Python字典,没有EnumMeta要求的_member_names属性,自然会触发属性错误。
  • 如果需要动态创建Enum子类,要么显式调用它的元类,要么用Enum官方提供的函数式API:
from enum import Enum, EnumMeta
# 方式1:显式调用元类
X2 = EnumMeta('X2', (Enum,), {})
# 方式2:使用Enum函数式API
X2 = Enum('X2', {})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 09:39:43