【问题标题】:Create a class dynamically without instantiating it - no metaclasses?动态创建一个类而不实例化它 - 没有元类?
【发布时间】:2017-10-31 13:44:07
【问题描述】:

以WTForms表单定义类为例:

class RegistrationForm(Form):
    username     = StringField('Username', [validators.Length(min=4, max=25)])
    email        = StringField('Email Address', [validators.Length(min=6, max=35)])
    accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])

查看库的源代码,WTForms 似乎允许用户定义一个非常简单的自定义表单结构类(如上),然后用于构造一个新的字段 在生成类时没有实例化的类。

我已经阅读了许多关于类工厂和元类的教程,普遍的共识是避免使用元类,而是使用类装饰器之类的东西。问题是教程要么开始导入额外的库,例如:import six,混合不同 Python 版本的解释,使用过于复杂的示例,或者建议根本不要使用元类。

请有人提供一个非常简单的解释(对于 Python 3),说明如何使用简单的类定义(如上面的 WTForms 示例)以及元类来自定义类的全新构造,而无需实际实例化类构建。

编辑:很抱歉很难解释我的最终目标是什么,但是当我浏览教程时,不清楚类装饰器、元类、魔术方法(调用newinit) 或这些的组合是我实现我所设想的东西所需要的,或者我所设想的东西是错误的做事方式事物。不幸的是,如果不了解实现目标所需的机制,似乎无法判断我的目标是否错误。我已经意识到元类是要走的路,只需要为一个非常简单的元类示例指明正确的方向,以 Python 3.x 的方式完成。

【问题讨论】:

  • 为什么您正在阅读的教程告诉您使用元类是不好的做法?元类有完全有效的用例。如果这就是所有教程所说的,IMO 他们错了。
  • 如果您更具体地说明您要完成的工作,参考您所读到的建议元类不符合目的的内容等,这将有所帮助。
  • 您是否可以提供一个示例链接来解释为什么不应该使用元类?这似乎不对。
  • 我有点怀疑教程实际上所说的是“在你接触元类之前,考虑一下你的元编程问题是否不能用更简单、更受限制的技术(如装饰器)来解决”。而不是“不要使用元类来制作 calasses”。
  • 另外——我真的检查了类装饰器覆盖的“90%”用例(或 99%,它只是在飞行中以任何方式制作的)。我不得不在“现实生活项目”中创建一些元类——我认为只有一次类装饰器就足够了。

标签: python python-3.x python-decorators metaclass class-factory


【解决方案1】:

您可以动态创建类 - 没有自定义元类,也没有装饰器,程序员看起来只是简单的函数调用。

只需使用三个参数调用 Python 的内置函数 type: 类的名称、带有其基的元组和带有命名空间的映射对象(即包含您通常在类主体上定义的属性和方法的字典)。

def __init__(self):
    ...

namespace = {
   '__init__': init,
   'name': 'default name'
}

MyClass = type("MyClass", (object,), namespace)

由于编译器在构建类主体中声明的函数期间做了一些特殊的事情,您失去了一些特性,这些特性只能使用 - 主要是使用无参数 super 和以 __ 开头的属性的名称修改的能力,但是就是这样。

也就是说,应该注意这不是“无元类”。 “type”本身就是一个元类——所有对象的默认 Python 元类——并且正在调用一个创建类的元类。没有其他方法可以创建类。 “类装饰器”只是一种可以在创建类对象后对其进行更改的方法。

任何产生新的动态类的函数或方法都将在其内部,在某些时候调用type 或其他元类。同样,“元类”本身不会创建动态类 - 它需要在类主体声明中使用,或者使用(至少)与调用 type 相同的参数调用。

关于“类装饰器”而不是元类的建议,我不确定是不是真的(事实上,“类装饰器”无法自行动态创建类):它们的主要缺点是对于装饰类的子类来说,将父类的装饰器自动应用到自己身上,这不是普通的方式,而元类是继承的。

在 Python 3.6 中,您有 __init_subclass__ 协议,是的,它可以避免元类的许多传统用途(但它仍然不会“动态创建类”——调用 type 会这样做)。

【讨论】:

  • 非常明确的答案,非常感谢。令人惊讶的是,有多少指南使用两个段落来解释“类型是一个类的一个类”等。然而,“类型本身就是一个元类”这句话有很大帮助。它当然解释了我需要专注于学习的内容以及我可以留到以后的内容。再次感谢@jsbueno
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-13
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
相关资源
最近更新 更多