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

模拟Django元类逻辑中DbMeta继承属性丢失问题及解决方法

问题:子类DbMeta为何没继承父类的属性?

我在模拟Django用元类实现数据库模型选项的逻辑时遇到了一个问题:子类的DbMeta类明明继承了父类的属性,但在实例化DatabaseOptions后却无法访问到继承来的属性。

我的测试代码

class DatabaseOptions(object):
    def __init__(self, opts):
        if opts:
            for key, val in opts.__dict__.items():
                if not key.startswith('__') and not callable(val):
                    setattr(self, key, val)

class MetaModel(type):
    def __new__(cls, name, bases, classdict):
        result = super().__new__(cls, name, bases, dict(classdict))
        opts = classdict.pop('DbMeta', None)
        if opts:
            setattr(result, '_db_meta', DatabaseOptions(opts))
        return result

class Model(object, metaclass=MetaModel):
    class DbMeta:
        database = 'default'
        migrate = True

class User(Model):
    class DbMeta(Model.DbMeta):
        database = 'user'

class GroupUser(User):
    class DbMeta(User.DbMeta):
        database = 'group_user'

预期输出

print(Model._db_meta.database) # default
print(Model._db_meta.migrate) # True
print(User._db_meta.database) # user
print(User._db_meta.migrate) # True
print(GroupUser._db_meta.database) # group_user
print(GroupUser._db_meta.migrate) # True

实际错误

default
True
user
Traceback (most recent call last):
  File "test.py", line 48, in <module>
    print(User._db_meta.migrate) # True
AttributeError: 'DatabaseOptions' object has no attribute 'migrate'

问题原因

核心问题出在DatabaseOptions.__init__里的opts.__dict__.items()调用上:

Python中,一个类的__dict__只包含该类自己定义的属性和方法,不包括从父类继承来的内容。比如User.DbMeta只定义了database,migrate是从Model.DbMeta继承的,所以它不在User.DbMeta.__dict__中,自然不会被复制到_db_meta对象里。


解决方案

方案1:用dir()遍历所有属性(包括继承的)

你已经找到的这个方案很有效,通过dir(opts)可以拿到类的所有可访问属性(包括继承的),再用getattr获取值:

class DatabaseOptions(object):
    def __init__(self, opts):
        if opts:
            for key in dir(opts):
                if not key.startswith('__'):
                    val = getattr(opts, key, None)
                    if not callable(val):
                        setattr(self, key, val)

方案2:遍历类的MRO(方法解析顺序)

如果需要处理更复杂的多层继承场景,可以遍历类的MRO(__mro__属性),逐个收集父类的属性,这样能确保所有继承链上的属性都被收集,且子类属性会覆盖父类的:

class DatabaseOptions(object):
    def __init__(self, opts):
        if opts:
            # 反向遍历MRO,先处理父类,再处理子类,子类属性覆盖父类
            for cls in reversed(opts.__mro__):
                for key, val in cls.__dict__.items():
                    if not key.startswith('__') and not callable(val):
                        setattr(self, key, val)

这两种方案都能解决你的问题,运行后就能得到预期的输出了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:05:28