繁体   English   中英

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

[英]Using metaclasses in order to define methods, class methods / instance methods

我试图更深入地了解元类在 python 中是如何工作的。 我的问题如下,我想使用元类来为每个 class 定义一个方法,该方法将使用在元类中定义的 class 属性。 例如,这有注册申请。

这是一个工作示例:

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 中定义一个方法。 因此,这是我可以在 object 上使用的方法。 我也可以使用 class 方法,因为它是在元类中定义的。 但是,尽管以下工作:

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 级别上定义方法,它也应该在 object 级别上定义。 但是,测试 3 引发了错误。

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

因此,你能解释一下我对 class 方法的理解是错误的吗? 我希望能够在print_register或 object 上调用 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)

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

另外,如果您对代码结构有任何提示,我将不胜感激。 我一定很不擅长元类的语法。

从根本上说,因为您已经在元类(您的类)的实例上print_register

因此,当您执行Foo.print_register时,它会找到您在中定义的print_register

class Foo(metaclass=register):
    ...
def print_register(self):
    pass

当然,这只是普通的 function 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'

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

cls.print_register = meta.print_register

这实际上就像在 class 定义中定义 function 一样......虽然不知道为什么要这样做。

您所做的事情与使用classmethod不同,它是一个自定义描述符,它以您需要能够在 class 或实例上调用它的方式处理方法与实例的绑定。 在 class 和实例上定义方法不同! 您可以在您的元类__new__中执行此操作,即cls.print_register = classmethod(meta.print_register)并将def print_register(self)排除在 class 定义之外:

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不必在元类中定义,实际上,在这种情况下,我只需在模块级别定义它:

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

...

我认为您对元类有足够的了解,实际上,据我所知,您对classmethod的理解是不正确的。 如果您想了解classmethod的工作原理,实际上,方法-实例绑定如何用于常规函数,您需要了解描述符 这是一个启发性的链接 Function 对象是描述符,它们在实例上调用时将实例作为第一个参数绑定到自身(相反,它们创建一个方法 object 并返回该方法,但它基本上是部分应用程序)。 classmethod对象是另一种描述符,它将 class 绑定到function的第一个参数,它在 class 或实例上调用时装饰。 该链接描述了如何使用纯classmethod编写类方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM