如何在 python 中使用元类来增加或覆盖添加到类中的方法

Posted

技术标签:

【中文标题】如何在 python 中使用元类来增加或覆盖添加到类中的方法【英文标题】:How do i augment or override a method that was added to a class , using a Metaclass in python 【发布时间】:2021-08-16 17:47:16 【问题描述】:

我正在尝试使用自定义元类向类添加方法,同时我希望能够覆盖或扩充从自定义元类创建的类中添加的方法(就像继承一样)。我似乎无法让它工作。

def print_func(obj,_str):
    print(_str)
class MetaClazz(type):
    def __new__(meta, classname, supers, classdict):
        classdict["print_func"] = print_func # method i want to add to the class
        return type.__new__(meta, classname, supers, classdict)

class Appset(metaclass=MetaClazz):
    def print_func(self,_str):
        # i want to update or overide the method so that the object made from the class can use updated version
        print("heyyy")
        Appset.print_func(self,_str)
        print("today is Friday")

# Inheritance
class Appset2:
    def print_func(self,_str):
        print(_str)
class Appset22(Appset2):
    def print_func(self,_str):
        print("heyyy")#override worked here
        Appset2.print_func(self,_str)
        print("today is Friday")
t_meta = Appset()
t_meta.print_func("Welcome to message For Metaclass \n")
t_inheritance = Appset22()
t_inheritance.print_func("Welcome to message For Inheritance \n")```

【问题讨论】:

我不太清楚你为什么要为此使用元类。继承变体显示了一个委托给父类方法的方法(顺便说一下,它应该使用super())。实际上,继承确保了同名方法可以有两个“版本”(一个在类上,一个在超类上)。在元类变体中,只有 一个 类可以拥有给定名称的 一个 方法——通过元类“添加”一个方法意味着 替换 任何以前的同名方法定义。元类不是继承的,为什么要这样使用它们呢? 只是为了纠正一个可能的混淆点:Appset.print_func 被定义之前 MetaClazzprint_func 添加到classdict。元类更新类的方法,而不是相反。 我知道它们不是一回事(元类和继承),但应该有一个构造元类可以用来在继承中实现同样的概念。需要明确的是,方法是扩充或覆盖不是我使用元类的主要原因。我只是想知道,如果它可能以及如何? 感谢您的最后一点。认为这样更清楚 通过继承,both 方法作为两个不同类的属性存在。在运行时,MRO 确定obj.print_func 实际解析到哪一个。另一方面,元类实际上是用全局函数替换了类语句中定义的函数。没有什么可以“继承”了。 【参考方案1】:

您要解决的问题是继承的自然工作方式。不需要元类,只需要一个基类,可以使用 Python 内置的super() 来执行祖先类中的方法:

class Base:
    def print_func(self, _str):
        print(_str)

class Appset(Base):
    def print_func(self,_str):
        # i want to update or overide the method so that the object made from the class can use updated version
        print("heyyy")
        super().print_func(_str)
        print("today is Friday")

# Inheritance
class Appset22(Appset2):
    def print_func(self,_str):
        print("heyyy")
        super().print_func(self,_str)
        print("today is Friday")
t_meta = Appset()
t_meta.print_func("Welcome to message For first level inheritance\n")
t_inheritance = Appset22()
t_inheritance.print_func("Welcome to message For second level inheritance \n")

虽然元类可以在类中注入新方法,但修改命名空间的直接方法(如您所做的那样)只会取代类主体中具有相同名称的任何内容。它们可用于为具有给定名称的所有方法应用装饰器,例如 - 在这种情况下,装饰器将通过包装来扩充类主体中定义的方法:在方法“外部”运行的代码。

【讨论】:

【参考方案2】:

您的元类忽略了在class 语句中定义的任何名为print_func 的函数,无条件地将其替换为全局函数print_func。当您定义MetaClazz.__new__print_func 时,只有一个名称,一旦您创建了一个新类,它的值就会根据正常的范围规则进行查找。在class 语句提供新方法之前,它不会充当“默认”方法;恰恰相反。

要创建MetaClazz 的实例可以覆盖的“默认”方法,请执行以下操作:

class MetaClazz(type):
    def __new__(meta, classname, supers, classdict):
        if "print_func" not in classdict:
            classdict["print_func"] = print_func
        return type.__new__(meta, classname, supers, classdict)

现在,只有在classdict(使用class 语句定义)还没有包含print_func 的整体时,才会将全局函数添加到类中。

【讨论】:

以上是关于如何在 python 中使用元类来增加或覆盖添加到类中的方法的主要内容,如果未能解决你的问题,请参考以下文章

python3 元类编程的一个例子

我可以使用 python 元类来跟踪单独文件中的子类吗?

如何在 Groovy 中动态覆盖类的“each”方法?

Python利用元类来控制实例创建

为啥 namedtuple 模块不使用元类来创建 nt 类对象?

python进阶详解元类及其应用2