如何在 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
被定义之前 MetaClazz
将print_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 中使用元类来增加或覆盖添加到类中的方法的主要内容,如果未能解决你的问题,请参考以下文章