Python元类和类装饰器的主要区别是什么?有没有一些只能通过其中一种方式实现的功能?
装饰器(Decorators)更加简单和有限,因此只要所需效果可以用元类或者类装饰器来实现的话,就应该优先考虑使用装饰器。
用类装饰器能做到的事情,在自定义元类中当然也能实现(只需要在元类的__new__
或 __init__
方法中应用“装饰器函数”的功能--即接受一个类对象并修改它的那个函数!)。
虽然自定义元类能做很多类装饰器不能做的事情(除非类装饰器内部生成并应用了自定义元类,当然这样做是作弊的;-)……而且即使如此,在Python3 中仍然有一些事情只能通过自定义元类来实现……但这属于比较高级的子领域,让我来给你举几个更简单的例子吧。
例如,假设你想创建一个类对象X
,使得print X
(或者在 Python 3 中则是print(X)
)输出peekaboo!
。你没有办法不用自定义元类来实现,因为这里关键的角色是元类中对__str__
方法的重载。也就是说,你需要在类X
的自定义元类中加入def __str__(cls): return "peekaboo!"
。
同样的道理适用于所有的魔法方法,即所有应用于类对象本身(而不是应用于其实例的操作,后者使用类中定义的魔法方法)的操作都会使用在元类中定义的魔法方法。
根据书籍“流畅的Python”第21章所述,一个差异与继承有关。请参见以下两个脚本。Python版本为3.5。其中一点是使用元类(metaclass)
会影响其子类,而装饰器(decorator)仅影响当前类。
该脚本使用类装饰器(class-decorator)来替换/覆盖方法'func1'。
def deco4cls(cls):
cls.func1 = lambda self: 2
return cls
@deco4cls
class Cls1:
pass
class Cls1_1(Cls1):
def func1(self):
return 3
obj1_1 = Cls1_1()
print(obj1_1.func1()) # 3
class Deco4cls(type):
def __init__(cls, name, bases, attr_dict):
# print(cls, name, bases, attr_dict)
super().__init__(name, bases, attr_dict)
cls.func1 = lambda self: 2
class Cls2(metaclass=Deco4cls):
pass
class Cls2_1(Cls2):
def func1(self):
return 3
obj2_1 = Cls2_1()
print(obj2_1.func1()) # 2!! the original Cls2_1.func1 is replaced by metaclass
__init__
方法。换句话说:这两个示例的行为不同,因为它们执行不同的操作。 - Gerrit Begher
foo(cls)
的装饰器,其中有两个语句:cls.__str__ = lambda self: "peekaboo!"
,然后紧接着是return cls
,它确实起作用了 - 我使用了这个装饰器为某个类设置了__str__
方法,而不是使用复杂的元类..我只是想说明一下,并可能获得一些评论,以了解是否漏掉了什么。 - rboyprint(X())
,即打印类X
的实例,并没有影响我在答案中非常明确提到的print(X)
,即打印类本身。请重新阅读我答案的最后一段。 - Alex Martelli