如何在类定义中向元类传递参数?

7 浏览
0 Comments

如何在类定义中向元类传递参数?

我正在尝试在Python 2.7中动态生成类,并想知道是否可以轻松地从类对象向元类传递参数。\n我已经阅读了这篇文章,非常棒,但还没有完全回答我的问题。目前我是这样做的:\n

def class_factory(args, to, meta_class):
    Class MyMetaClass(type):
        def __new__(cls, class_name, parents, attrs):
            attrs['args'] = args
            attrs['to'] = to
            attrs['eggs'] = meta_class
    class MyClass(object):
        metaclass = MyMetaClass
        ...

\n但这要求我这样做:\n

MyClassClass = class_factory('spam', 'and', 'eggs')
my_instance = MyClassClass()

\n有没有更简洁的方法?

0
0 Comments

如何从类定义中将参数传递给元类?这个问题的出现是因为在Python中,元类(metaclass)是用来创建类的类。通过指定一个类的元类,可以对该类进行特殊的处理和定制。然而,在某些情况下,我们希望能够从类定义中将参数传递给元类,以便在创建类时进行一些特定的操作。

解决这个问题的方法是在元类的__new__()方法中检查传递给最后一个参数的类字典。在class语句中定义的任何内容都将在其中。下面是一个示例代码:

class MyMetaClass(type):
    def __new__(cls, class_name, parents, attrs):
        if 'meta_args' in attrs:
            meta_args = attrs['meta_args']
            attrs['args'] = meta_args[0]
            attrs['to'] = meta_args[1]
            attrs['eggs'] = meta_args[2]
            del attrs['meta_args'] # clean up
        return type.__new__(cls, class_name, parents, attrs)
class MyClass(object):
    __metaclass__ = MyMetaClass
    meta_args = ['spam', 'and', 'eggs']
myobject = MyClass()
from pprint import pprint
pprint(dir(myobject))
print myobject.args, myobject.to, myobject.eggs

在上述代码中,我们定义了一个元类MyMetaClass,并在其中的__new__()方法中检查了类定义中的meta_args属性,并将其分别赋值给args、to和eggs属性。然后我们创建了一个类MyClass,并将元类指定为MyMetaClass。类定义中的meta_args属性将被传递给元类,并在创建类时进行处理。最后,我们实例化了MyClass类,并打印了实例的属性。

以上代码的输出为:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'args', 'eggs', 'to']
spam and eggs

需要注意的是,上述代码只适用于Python 2,因为在Python 3中指定元类的语法发生了不兼容的改变。在Python 3中,可以通过更改MyClass的定义来使其工作:

class MyClass(metaclass=MyMetaClass):
    meta_args = ['spam', 'and', 'eggs']

此外,还可以通过在Python 2和3中都能工作的方式来解决语法差异,并在定义中显式调用元类并将创建的类用作正在定义的类的基类:

class MyClass(MyMetaClass("NewBaseClass", (object,), {})):
    meta_args = ['spam', 'and', 'eggs']

值得注意的是,Python 3中的类构造方式也已经发生了修改,并增加了对其他传递参数方式的支持。在某些情况下,使用这些方式可能比上述示例中的技术更加简单和方便。具体取决于您想要实现的功能。

以上是关于如何从类定义中将参数传递给元类的原因和解决方法的总结。通过在元类的__new__()方法中检查类定义中的属性,并在创建类时进行处理,我们可以实现将参数传递给元类的功能。

0
0 Comments

如何从类定义向元类传递参数?

在Python 3.3中,我有与Python 2.7中相同的问题,这个线程是我在谷歌上找到的与此问题最接近的答案。通过研究Python文档,我找到了一个更好的解决方案,现在我将我的发现分享给其他在这里寻找Python 3.x版本的人。

在Python 3.x中传递参数给元类

通过在类声明中添加额外的关键字参数,可以在Python 3.x中传递参数给元类:

class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):

pass

这些参数会这样传递给元类:

class MyMetaClass(type):

def __prepare__(metacls, name, bases, **kwargs):

return super().__prepare__(name, bases, **kwargs)

def __new__(metacls, name, bases, namespace, **kwargs):

return super().__new__(metacls, name, bases, namespace)

def __init__(cls, name, bases, namespace, myArg1=7, **kwargs):

super().__init__(name, bases, namespace)

在调用type.__new__和type.__init__时,必须将kwargs从中排除掉,否则会由于传递了太多参数而引发TypeError异常。这意味着当以这种方式传递元类参数时,我们总是需要实现MyMetaClass.__new__和MyMetaClass.__init__,以防止我们自定义的关键字参数传递到基类type.__new__和type.__init__方法中。type.__prepare__似乎可以优雅地处理额外的关键字参数(因此在示例中将它们传递,以防有些我不知道的依赖于kwargs的功能),所以定义type.__prepare__是可选的。

在Python 3.6中,type被调整,type.__init__现在可以优雅地处理额外的关键字参数。仍然需要定义type.__new__(抛出TypeError: __init_subclass__() takes no keyword arguments异常)。

在Python 3中,通过关键字参数而不是类属性来指定元类:

class MyClass(metaclass=MyMetaClass):

pass

这个语句大致等同于:

MyClass = metaclass(name, bases, **kwargs)

其中metaclass是你传入的"metaclass"参数的值,name是你的类的字符串名称('MyClass'),bases是你传入的任何基类(在这种情况下是一个长度为零的元组()),kwargs是任何未捕获的关键字参数(在这种情况下是一个空字典{})。

进一步分解,这个语句大致等同于:

namespace = metaclass.__prepare__(name, bases, **kwargs)

MyClass = metaclass.__new__(metaclass, name, bases, namespace, **kwargs)

metaclass.__init__(MyClass, name, bases, namespace, **kwargs)

其中kwargs始终是我们传递给类定义的未捕获关键字参数的字典。

关于上述示例的进一步分解:

class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):

pass

大致等同于:

namespace = MyMetaClass.__prepare__('C', (), myArg1=1, myArg2=2)

C = MyMetaClass.__new__(MyMetaClass, 'C', (), namespace, myArg1=1, myArg2=2)

MyMetaClass.__init__(C, 'C', (), namespace, myArg1=1, myArg2=2)

大部分信息来自Python关于"Customizing Class Creation"的文档。

这非常有帮助。我希望你能提出一个关于Python 3的新问题,然后自己回答它 - 我很难找到它。

值得一提的是,即使你只是继承了一个已经指定了自定义元类的类,你也可以添加额外的kwargs。例如class D(C, myArg2=2)。

微妙的一点是,当从重写的__init__()中调用super().__init__()时,最好将**kwargs传递给后者,即使它通常为空,这样它可以在它不为空时引发TypeError: __init_subclass__() takes no keyword arguments异常。还请注意,这意味着在自定义的__init__()中处理的任何关键字参数都需要从kwargs中删除,以避免将它们传递出去。以这种方式完成后,代码应该继续工作,即使type.__init__()以后开始接受一些参数。

0