使用 metaclasses 来定义方法,class 方法/实例方法

Using metaclasses in order to define methods, class methods / instance methods

我试图更深入地了解 metaclasses 在 python 中的工作原理。我的问题如下,我想使用 metaclasses 来为每个 class 定义一个方法,该方法将使用在 meta[ 中定义的 class 属性=47=]. 比如这个有注册申请.

这是一个工作示例:

import functools


def dec_register(func):
    @functools.wraps(func)
    def wrapper_register(*args, **kwargs):
        (args[0].__class__.list_register_instances).append(args[0])
        return func(*args, **kwargs)
    return wrapper_register




dict_register_classes = {}
class register(type):
    def __new__(meta, name, bases, attrs):
        dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs)  # assigniation from right to left
        cls.list_register_instances = []
        cls.print_register = meta.print_register
        return cls

    def print_register(self):
        for element in self.list_register_instances:
            print(element)

    def print_register_class(cls):
        for element in cls.list_register_instances:
            print(element)

#
class Foo(metaclass=register):
    @dec_register
    def __init__(self):
        pass

    def print_register(self):
        pass

class Boo(metaclass=register):
    @dec_register
    def __init__(self):
        pass
    def print_register(self):
        pass

f = Foo()
f_ = Foo()
b = Boo()
print(f.list_register_instances)
print(b.list_register_instances)
print(dict_register_classes)

print("1")
f.print_register()
print("2")
Foo.print_register_class()
print("3")
f.print_register_class()
print("4")
Foo.print_register()

我最后做的测试没有像我预期的那样工作。如果我说的没有使用正确的语法,我提前道歉,我会尽可能清楚:

我认为 cls.print_register = meta.print_register 行是在 class 中使用元 class 中定义的方法定义方法。因此,这是一种我可以在对象上使用的方法。我也可以使用它作为 class 方法,因为它是在 metaclass 中定义的。然而,虽然以下工作:

print("1")
f.print_register()

这不能正常工作:

print("4")
Foo.print_register()

错误:

Foo.print_register()
TypeError: print_register() missing 1 required positional argument: 'self'

与测试 2 和 3 相同,我期望如果在 class 级别上定义了一个方法,那么它也应该在对象级别上定义。但是,测试 3 出现错误。

print("2")
Foo.print_register_class()
print("3")
f.print_register_class()

因此,您能解释一下我对 class 方法的理解为什么是错误的吗?我希望能够在 class 或对象上调用方法 print_register


也许知道我实际上是在尝试重现以下非常简单的示例可能会有所帮助:

# example without anything fancy:
class Foo:
    list_register_instances = []
    def __init__(self):
        self.__class__.list_register_instances.append(self)

    @classmethod
    def print_register(cls):
        for element in cls.list_register_instances:
            print(element)

我不是在用 metaclass 做同样的事情吗? class 方法可以用于 class 或对象。

此外,如果您有任何关于代码结构的提示,我将不胜感激。我一定很不擅长 metaclasses.

的语法

从根本上说,因为你的元class(你的class)实例上有阴影print_register

所以当您执行 Foo.print_register 时,它会找到您在

中定义的 print_register
class Foo(metaclass=register):
    ...
def print_register(self):
    pass

当然,这只是普通函数 print_register,它需要 self 参数。

这(几乎)与常规 class 发生的事情相同,它是实例:

class Foo:
    def bar(self):
        print("I am a bar")

foo = Foo()

foo.bar = lambda x: print("I've hijacked bar")

foo.bar()

注:

In [1]: class Meta(type):
   ...:     def print_register(self):
   ...:         print('hi')
   ...:

In [2]: class Foo(metaclass=Meta):
   ...:     pass
   ...:

In [3]: Foo.print_register()
hi

In [4]: class Foo(metaclass=Meta):
   ...:     def print_register(self):
   ...:         print('hello')
   ...:

In [5]: Foo.print_register()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-a42427fde947> in <module>
----> 1 Foo.print_register()

TypeError: print_register() missing 1 required positional argument: 'self'

但是,您也在元class 构造函数中执行此操作!

cls.print_register = meta.print_register

这实际上就像在您的 class 定义中定义该函数...虽然不确定您为什么要这样做。

不是做与使用 classmethod 完全相同的事情,它是一个自定义描述符,按照您的方式处理方法与实例的绑定需要能够在 class 或实例上调用它。那是 not 与在 class 和实例上定义方法相同!您可以在您的元class __new__ 中执行此操作,即 cls.print_register = classmethod(meta.print_register) 并在您的 class 定义中保留 def print_register(self)

import functools

def dec_register(func):
    @functools.wraps(func)
    def wrapper_register(*args, **kwargs):
        (args[0].__class__.list_register_instances).append(args[0])
        return func(*args, **kwargs)
    return wrapper_register


dict_register_classes = {}
class register(type):
    def __new__(meta, name, bases, attrs):
        dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs)  # assigniation from right to left
        cls.list_register_instances = []
        cls.print_register = classmethod(meta.print_register) # just create the classmethod manually!
        return cls

    def print_register(self):
        for element in self.list_register_instances:
            print(element)

    def print_register_class(cls):
        for element in cls.list_register_instances:
            print(element)

#
class Foo(metaclass=register):
    @dec_register
    def __init__(self):
        pass

请注意,print_register 不必在您的 metaclass 中定义,事实上,在这种情况下,我只是在模块级别定义它:

def print_register(self):
    for element in self.list_register_instances:
        print(element)
...

class register(type):
    def __new__(meta, name, bases, attrs):
        dict_register_classes[name] = cls = type.__new__(meta, name, bases, attrs)  # assigniation from right to left
        cls.list_register_instances = []
        cls.print_register = classmethod(print_register) 
        return cls

...

我认为你对metaclasses的理解足够了,实际上,据我所知,你对classmethod的理解是不正确的。如果你想了解 classmethod 是如何工作的,事实上,方法实例绑定是如何为常规函数工作的,你需要了解 描述符 Here's an enlightening link。函数对象是描述符,它们在实例上调用时将实例作为第一个参数绑定到它们自己(相反,它们创建一个方法对象和 return 那个,但它基本上是部分应用程序)。 classmethod 对象是另一种描述符,它将 class 绑定到它修饰的函数的第一个参数 class 或实例。 link 描述了如何使用纯 python.

编写 classmethod