使用 python 元类和继承的问题

     2023-02-23     52

关键词:

【中文标题】使用 python 元类和继承的问题【英文标题】:Issues with using python metaclasses and inheritence 【发布时间】:2020-07-26 10:15:25 【问题描述】:

我一直在为一个项目开发元类布局,其中所有类都使用自定义元类来加载他们定义的配置以及父类的配置。基本上每个类都定义了一个嵌套的Config 类,该类被加载到一个字典中,然后子类也可以定义一个,并且该类使用所有父配置并覆盖任何新值。

当我在将 Config 类加载到 dict 后不删除它时,它工作得很好,但现在我正在尝试重构和清理命名空间,但它会导致问题。新(破)代码如下:

class AbstractConfigMeta(ABCMeta):
    """Parse nested Config classes and fill a new classes config dict."""

    def __new__(mcs, name, bases, namespace):
        """Traverse the MRO backwards from object to load all Config classes.

        Any config declared in sub classes overwrites base classes.
        """
        # get any config defined in parent classes first
        config = 
        for parent in reversed(bases):
            if hasattr(parent, "config"):
                config.update(parent.config)
        # pop Config class and add values if defined
        config_class = namespace.pop("Config", None)
        if config_class:
            # get all non-magic (i.e. user-defined) attributes
            attributes = 
                key: value
                for key, value in config_class.__dict__.items()
                if not key.startswith("__")
            
            config.update(attributes)

        namespace["config"] = config
        return super().__new__(mcs, name, bases, namespace)

它在使用时会解析 Config 类,但现在不使用来自父母的任何配置。实例化后有效但保留嵌套类的旧代码是:

class AbstractConfigMeta(ABCMeta):
    """Parse nested Config classes and fill a new classes config dict."""

    def __new__(mcs, name, bases, namespace):
        """Traverse the MRO backwards from object to load all Config classes.

        Any config declared in sub classes overwrites base classes.
        """
        new_class = super().__new__(mcs, name, bases, namespace)
        new_class.config =   # type: ignore

        for parent in reversed(new_class.__mro__):
            config_class = getattr(parent, "Config", None)
            if config_class:
                # get all non-magic attributes from each Config class
                values = 
                    key: value
                    for key, value in config_class.__dict__.items()
                    if not key.startswith("__")
                
                new_class.config.update(values)  # type: ignore
        return new_class

现在似乎尝试使用元类创建的字典访问配置,父配置被丢弃。任何帮助将不胜感激。

更新

这个问题原来是由一些使用嵌套 Config 类但不使用元类的 Mixin 引起的。这在旧代码块中很好,但是当更改为从配置字典而不是嵌套类获取父配置时,任何不使用元类的东西都不会定义这个,所以会有一个不使用值的 Config 类。

最终的工作代码,包括 jsbueno 建议的修复和覆盖边缘情况:

class AbstractConfigMeta(ABCMeta):
    """Parse nested Config classes and fill a new classes config dict."""

    def __new__(mcs, name, bases, namespace):
        """Traverse the MRO backwards from object to load any config dicts.

        Any Config class declared in sub classes overwrites parent classes.
        """
        # pop Config class and add its attributes if defined
        config_class = namespace.pop("Config", None)
        if config_class:
            # get all non-magic (i.e. user-defined) attributes
            attributes = 
                key: value
                for key, value in config_class.__dict__.items()
                if not key.startswith("__")
            
            if namespace.get("config"):
                warnings.warn(
                    f"A config dict and a config class are defined for name."
                    + " Any values in the config dict will be overwritten."
                )
            namespace["config"] = attributes

        new_class = super().__new__(mcs, name, bases, namespace)
        # get any config dicts defined in the MRO (including the current class)
        config = 
        for parent in reversed(new_class.__mro__):
            if hasattr(parent, "config"):
                config.update(parent.config)  # type: ignore

        new_class.config = config  # type: ignore
        return new_class

【问题讨论】:

【参考方案1】:

问题在于,在新代码中,您通过类显式bases 进行交互,而旧(工作)代码迭代__mro__

bases 将只产生显式声明的祖先,并且不会访问任何“祖父母”或更复杂层次结构中的类。

要走的路是允许 Python 生成 __mro__,方法是实际创建您的新类,并迭代以检索新类上的配置键。 config 属性可以只在新创建的类上设置 - 无需在命名空间中这样做。

不建议尝试复制 Python 的 __mro__ - 这是一个相当复杂的算法,即使你一步一步地做对了,你也只是在重新发明***。

所以,还有一些事情:

class AbstractConfigMeta(ABCMeta):
    """Parse nested Config classes and fill a new classes config dict."""

    def __new__(mcs, name, bases, namespace):
        """Traverse the MRO backwards from object to load all Config classes.

        Any config declared in sub classes overwrites base classes.
        """


        config_class = namespace.pop("Config", None)

        cls = super().__new__(mcs, name, bases, namespace)
        # get any config defined in parent classes first
        config = 

        for parent in reversed(cls.__mro__):
            # Keep in mind this also runs for `cls` itself, so "config" can
            # also be specced as a dictionary. If you don't want that
            # to be possible, place a condition here to raise if `parent is cls and hasattr...`
            if hasattr(parent, "config"):
                config.update(parent.config)
        # pop Config class and add values if defined

        if config_class:
            # get all non-magic (i.e. user-defined) attributes
            attributes = 
                key: value
                for key, value in config_class.__dict__.items()
                if not key.startswith("__")
            
            config.update(attributes)

        cls.config = config
        return cls

【讨论】:

感谢您的回复!事实证明,失败的测试是由不使用元类但确实定义了 Config 类的 mixin 引起的。但是,您提出了 2 个优点,即 mro 比这里的基础更好,并且可能与定义的 dict 和类发生冲突,现在这些已得到修复。我将为任何偶然发现此问题的其他人发布带有工作代码的编辑。 使用 bases 而不是 __mro__ 会导致优先级不一致,但如果没有 mixin 元类错误,所有祖先配置仍然会被考虑。 它们将被访问,但在之前的传递中,并将结果“累积”在直接父类中。所以,是的,它确实适用于大多数情况,并且如果类配置在创建之后和创建孙子之前发生更改,则会出现一些奇怪的行为。

将元类与多重继承结合使用的 TypeErrors

】将元类与多重继承结合使用的TypeErrors【英文标题】:TypeErrorsusingmetaclassesinconjunctionwithmultipleinheritance【发布时间】:2011-01-1307:42:18【问题描述】:我有两个关于元类和多重继承的问题。第一个是:为什么我会得到类Derived的TypeE... 查看详情

参数化类和元类有啥区别(请使用 Python 中的代码示例)?

】参数化类和元类有啥区别(请使用Python中的代码示例)?【英文标题】:Whatisthedifferencebetweenaparameterizedclassandametaclass(codeexamplesinPythonplease)?参数化类和元类有什么区别(请使用Python中的代码示例)?【发布时间】:2011-03-3014:09... 查看详情

python元类继承问题

...:53【问题描述】:我有一个有点奇怪的元类问题。我正在使用元类动态创建一个继承自另一个超类的“兄弟”类,并将其分配为原始类的属性。下面是一个最小的设置:classMeta(type):def__new__(cls,name,parents,dct):sdct=dct.copy()dct[\'s 查看详情

Python2(有六个)元类和带参数的字段

...间】:2017-05-0514:12:29【问题描述】:我正在创建一个应该使用字段的元类。基于互联网上的资源,在***上我已经走到了这一步:元类defgetmethod(attrname):def_getmethod(self):returngetattr(self,"__"+ 查看详情

是否可以使用 Python 元类进行反向继承?

】是否可以使用Python元类进行反向继承?【英文标题】:IsitpossibletoinverseinheritanceusingaPythonmetaclass?【发布时间】:2016-11-1904:46:10【问题描述】:出于好奇,我很感兴趣是否可以编写一个元类,使父类的方法优先于子类的方法。我... 查看详情

旧式类、新式类和元类

...dmetaclasses【发布时间】:2012-05-1617:09:18【问题描述】:在Python2.x中,所有新样式的类都隐式或显式地继承自object。然后看看这个:>>>classM(type):...pass...>>>classA:...__metaclass__=M...>& 查看详情

Python 元类和 ModGrammar

】Python元类和ModGrammar【英文标题】:PythonmetaclassandModGrammar【发布时间】:2011-10-2706:05:25【问题描述】:我发现(在***上的另一个问题之后)这个用Python编写的有趣库,其目标是语法解析。http://code.google.com/p/modgrammar/我还找到了... 查看详情

复习打卡--0819元类和内存管理(代码片段)

...,所有类的类型都是type。所有的类都是type创建出来的;#使用type动态地创建类deffunc1(self):print(self.name)dict1="name":"lala","age":18,"func":func1()my=type("Myclass",bases=(object,),dict=dict1)#第一个参数为:类名,第二个参数为继承的类,第三个参... 查看详情

从 Python 中的元类继承

】从Python中的元类继承【英文标题】:InheritancefrommetaclassesinPython【发布时间】:2022-01-1807:49:01【问题描述】:我有一个简单的元类,它将以“get_”开头的类的方法转换为属性:classPropertyConvertMetaclass(type):def__new__(mcs,future_class_nam... 查看详情

单例类和元类

】单例类和元类【英文标题】:Singletonclassandmetaclass【发布时间】:2014-08-0507:30:21【问题描述】:我知道单例类是什么。单例类用于保存为对象定义的单例方法。元类和单例类是一样的吗?谁能解释一下什么是元类?关于元类的... 查看详情

Python 元类与类装饰器

...装饰器要简单得多,也受到更多限制——因此,只要可以使用元类或类装饰器来实现所需的效果,就应该首选装饰器。任何你可以用类装饰器做的事情,你当然可以 查看详情

04--元类和orm(代码片段)

...创建类等;以及ORM,即什么是ORM等知识一、元类  1.1在Python中一切皆对象  在学习元类中我们首先需要了解一个概念-- python中一切皆对象,那么为什么这么说呢?  扩展:通过globals()--查看所有的全局变量,当我们定... 查看详情

如何在 Python 中构建继承类的分层视图?

...我终于无法逃避这个话题。我尝试了各种解决方案并决定使用其中一种,并希望与您分享。互联网上的许多解决方案根本不起作用,我认为它可以帮助那些对类和元类不太流利的人。我 查看详情

避免使用元类继承生成的类属性

】避免使用元类继承生成的类属性【英文标题】:Avoidinheritinggeneratedclassattributesusingmetaclass【发布时间】:2017-10-0709:34:25【问题描述】:我正在考虑使用元类自动将子类添加到父类以“链接”。但是,从父类继承这些属性会使事... 查看详情

习惯从类型继承元类?

...时间】:2011-01-1003:03:42【问题描述】:我一直在尝试理解python元类,因此一直在浏览一些示例代码。据我了解,Python元类可以是任何可调用的。所以,我可以让我的元类像defmetacls(clsName,bases,atts):....returntype(clsName,bases,atts)但 查看详情

Peewee Model 和 QtCore QObject 的多重继承导致元类冲突

...在这里做错了什么,请告诉我,我很乐意修复它。我正在使用Python2.7.15rc1和P 查看详情

在 python3.x 中显式继承“类型”以实现元类

】在python3.x中显式继承“类型”以实现元类【英文标题】:Explicitlyinheritingfrom\'type\'toimplementmetaclassinpython3.x【发布时间】:2018-04-2622:35:26【问题描述】:我试图对Python中的元类有一些直觉。我已经尝试过Python2.7和Python3.5。在Python... 查看详情

Python 元类教程

】Python元类教程【英文标题】:Pythonmetaclassestutorial【发布时间】:2011-08-0415:26:04【问题描述】:很抱歉,如果我不得不将您用作Google,但Google无济于事。我一直在寻找关于Python中元类的非常好的教程。我记得它的一个功能是一... 查看详情