Python 使用元类定义方法、类方法/实例方法

Python 使用元类定义方法、类方法/实例方法,python,python-3.x,class,metaclass,class-method,Python,Python 3.x,Class,Metaclass,Class Method,我试图更深入地理解元类在python中是如何工作的。我的问题如下,我想使用元类为每个类定义一个方法,该方法将使用在元类中定义的class属性。例如,这有注册应用程序 以下是一个工作示例: import functools def dec_register(func): @functools.wraps(func) def wrapper_register(*args, **kwargs): (args[0].__class__.list_register_in

我试图更深入地理解元类在python中是如何工作的。我的问题如下,我想使用元类为每个类定义一个方法,该方法将使用在元类中定义的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\u register=meta.print\u register
使用在元类中定义的方法在类中定义方法。因此,这是一种我可以在对象上使用的方法。我也可以使用它作为类方法,因为它是在元类中定义的。然而,尽管以下工作:

print("1")
f.print_register()
这不能正常工作:

print("4")
Foo.print_register()
有误:

Foo.print_register()
TypeError: print_register() missing 1 required positional argument: 'self'
测试2和测试3也是如此,我希望如果在类级别定义了一个方法,那么它也应该在对象级别定义。然而,测试3产生了一个错误

print("2")
Foo.print_register_class()
print("3")
f.print_register_class()
因此,你能解释一下为什么我对类方法的理解是错误的吗?我希望能够在类或对象上调用方法
print\u 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)
我对元类不是做了完全相同的事情吗?classmethod既可以用于类,也可以用于对象


如果您对代码结构有任何建议,我将不胜感激。我对元类的语法一定很差。

基本上,因为您在元类实例(您的类)上隐藏了
print\u register

因此,当您执行
Foo.print\u register
时,它会找到您在中定义的
print\u register

class Foo(metaclass=register):
    ...
def print_register(self):
    pass
当然,这只是普通函数
print\u register
,它需要
self
参数

这(几乎)与普通类及其实例发生的情况相同:

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

foo = Foo()

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

foo.bar()
注:

这实际上就像在类定义中定义那个函数一样。。。不知道你为什么要这么做

您所做的事情与使用
classmethod
完全不同,后者是一个自定义描述符,用于处理方法到实例的绑定,就像您需要在类或实例上调用它一样。这与在类和实例上定义方法不同!您可以在元类
\uuuu new\uuuu
中执行此操作,即
cls.print\u register=classmethod(meta.print\u register)
并将
def print\u 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\u 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
是如何工作的,实际上,方法实例绑定是如何为常规函数工作的,那么您需要了解描述符。函数对象是描述符,当对实例调用时,它们将实例作为第一个参数绑定到自身(相反,它们创建一个方法对象并返回该对象,但它基本上是部分应用程序)
classmethod
对象是另一种描述符,它将类绑定到它在类或实例上调用时所修饰的函数的第一个参数。该链接描述了如何使用纯python编写
classmethod

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
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

...