gpt4 book ai didi

python - 如何从类定义中将参数传递给元类?

转载 作者:IT老高 更新时间:2023-10-28 21:16:36 25 4
gpt4 key购买 nike

我正在尝试在 python 2.7 中动态生成类,并且想知道您是否可以轻松地将参数从类对象传递给元类。

我已阅读 this帖子,这很棒,但并没有完全回答这个问题。目前我正在做:

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

但这需要我做以下事情

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

有没有更清洁的方法?

最佳答案

虽然这个问题是针对 Python 2.7 的,并且已经有了很好的答案,但我对 Python 3.3 也有同样的问题,而且这个帖子是我在 Google 上找到的最接近答案的帖子。通过深入研究 Python 文档,我为 Python 3.x 找到了更好的解决方案,并且我正在与其他来这里寻找 Python 3.x 版本的人分享我的发现。

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

在翻阅 Python 的官方文档后,我发现 Python 3.x 提供了一种将参数传递给元类的 native 方法,尽管并非没有缺陷。

只需在类声明中添加额外的关键字参数:

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

...它们会像这样传递到您的元类中:

class MyMetaClass(type):

@classmethod
def __prepare__(metacls, name, bases, **kwargs):
#kwargs = {"myArg1": 1, "myArg2": 2}
return super().__prepare__(name, bases, **kwargs)

def __new__(metacls, name, bases, namespace, **kwargs):
#kwargs = {"myArg1": 1, "myArg2": 2}
return super().__new__(metacls, name, bases, namespace)
#DO NOT send "**kwargs" to "type.__new__". It won't catch them and
#you'll get a "TypeError: type() takes 1 or 3 arguments" exception.

def __init__(cls, name, bases, namespace, myArg1=7, **kwargs):
#myArg1 = 1 #Included as an example of capturing metaclass args as positional args.
#kwargs = {"myArg2": 2}
super().__init__(name, bases, namespace)
#DO NOT send "**kwargs" to "type.__init__" in Python 3.5 and older. You'll get a
#"TypeError: type.__init__() takes no keyword arguments" exception.

您必须将 kwargs 排除在对 type.__new__type.__init__ 的调用之外(Python 3.5 和更早版本;请参阅“更新") 或者会因为传递太多参数而导致 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__() 没有关键字参数 异常)。

故障

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

class MyClass(metaclass=MyMetaClass):
pass

这句话大致翻译为:

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

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

进一步分解,该语句大致翻译为:

namespace = metaclass.__prepare__(name, bases, **kwargs)  #`metaclass` passed implicitly since it's a class method.
MyClass = metaclass.__new__(metaclass, name, bases, namespace, **kwargs)
metaclass.__init__(MyClass, name, bases, namespace, **kwargs)

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

分解我上面给出的例子:

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

...大致翻译为:

namespace = MyMetaClass.__prepare__('C', (), myArg1=1, myArg2=2)
#namespace={'__module__': '__main__', '__qualname__': 'C'}
C = MyMetaClass.__new__(MyMetaClass, 'C', (), namespace, myArg1=1, myArg2=2)
MyMetaClass.__init__(C, 'C', (), namespace, myArg1=1, myArg2=2)

大部分信息来自Python's Documentation on "Customizing Class Creation" .

关于python - 如何从类定义中将参数传递给元类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13762231/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com