42

Python 元类和类装饰器之间的主要区别是什么?有什么我可以用一个做但不能用另一个做的吗?

4

2 回答 2

46

装饰器要简单得多,也更受限制——因此,只要可以使用元类或类装饰器来实现所需的效果,就应该首选装饰器。

任何你可以用类装饰器做的事情,你当然可以用一个自定义元类来做(只需应用“装饰器函数”的功能,即在元类的过程中接受一个类对象并修改它的那个__new____init__使类对象!-)。

您可以在自定义元类中做很多事情,但不能在装饰器中做(当然,除非装饰器在内部生成并应用自定义元类——但那是作弊;-)......即使那样,在 Python 3 中,还有是你只能用自定义元类做的事情,而不是事后......但这是你问题的一个非常高级的子领域,所以让我举个更简单的例子)。

例如,假设您想要创建一个类对象X,以便print X(或者在 Python 3print(X)中;-)显示peekaboo!. 如果没有自定义元类,您不可能做到这一点,因为元类的覆盖__str__是这里的关键角色,即您需要def __str__(cls): return "peekaboo!"在自定义元类中的 class X

这同样适用于所有魔术方法,即应用于类对象本身的各种操作(与应用于其实例的操作相反,它们使用类中定义的魔术方法 - 对类对象本身的操作使用元类中定义的魔法方法)。

于 2009-11-22T17:49:49.983 回答
3

正如“fluent python”一书的第 21 章所述,一个区别与继承有关。请看这两个脚本。python版本是3.5。一点是使用metaclass会影响其子类,而装饰器仅影响当前类。

该脚本使用类装饰器来替换/覆盖方法“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

该脚本使用元类来替换/覆盖方法“func1”。

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
于 2020-05-14T06:55:51.197 回答