【问题标题】:Using the same class in the class attribute definition在类属性定义中使用同一个类
【发布时间】:2022-01-16 01:24:51
【问题描述】:

我怎样才能做出这样的事情?

class Form(object, metaclass=...):
    pass


class Field(object):
    def __init__(self, nested_form_class=None):
        self.nested_form_class = nested_form_class


class TaskForm(Form):
    children = Field(nested_form_class=TaskForm)

渲染:

NameError: name 'TaskForm' is not defined

错误发生在定义children 属性的行上。

我在元类的__new__ 函数中使用了类属性,我无法将其移动到当前类的__init__ 函数中。

我的元类(就像在 django 框架中一样):

class FormMeta(type):
    """
    Meta class for extract fields from model
    """

    def __new__(mcs, name, bases, attrs):
        found_fields = dict()

        for k, v in attrs.items():
            if isinstance(v, Field):
                found_fields[k] = v

        attrs['found_fields'] = found_fields

        new_class = super().__new__(mcs, name, bases, attrs)

        parent_fields = {}

        for base in reversed(new_class.__mro__):
            # Collect fields from base class.
            if hasattr(base, 'found_fields'):
                parent_fields.update(base.found_fields)

            # Disable reordered fields.
            for attr, value in base.__dict__.items():
                if value is None and attr in parent_fields:
                    parent_fields.pop(attr)

        new_class.base_fields = parent_fields
        new_class.found_fields = parent_fields

        return new_class

【问题讨论】:

  • 你能继承原来的元类吗?
  • 另外,您介意向我们展示一下元类的定义吗?
  • @DanielWalker ,我添加了元类。谢谢!是的。我可以更改元类。我正在为我的项目开发新的表单库。
  • 你不能引用一个还不存在的类,但你可以给它类名,并在它第一次使用时从名字中获取类? IE。 getattr(sys.modules[module_name], classname) 如果必须的话。但是,如果在类完成之前发生这种情况,您仍然会遇到令人讨厌的问题,这可能是因为您无法控制 Field 构造函数。您实际上想解决什么问题?
  • @Grismar,我想为自定义表单库创建方便的代码。该表单可以使用递归呈现模型字段。我希望儿童模型使用相同的表单类来创建子子表单。我将在实例化时使用表单类属性。我想我可以从模块中按名称获得真正的课程。我想了=)谢谢。

标签: python metaclass


【解决方案1】:

class 语句体中,被声明的类还不存在 - 因此不能像您的示例中那样引用它。 Django 和 Python PEP 484 中类型注释的原始使用通过允许您将类名改为字符串来解决此问题 - 但当您想要对类的简单引用时,这无济于事。

Python 中的“描述符协议”——如果在旨在用作字段的事物的类中实现的少数方法——就像在您的情况下一样,由 Python 自动调用。 __get____set____delete__ 方法在属性访问时调用,__set_name__ 在创建类时调用,并传递所有者类。您可以将其用于您的 Field 课程。您的元类不需要更改。 (请记住,__set_name__type.__new__ 执行结束时调用,在元类__init__ 运行之前。):

class Form(object, metaclass=...):
    pass


class Field(object):
    def __init__(self, nested_form_class=None):
        self.nested_form_class = nested_form_class

    def __set_name__(self, owner, name):
         if self.nested_form_class is None:
            self.nested_form_class = owner
         self.name = name


class TaskForm(Form):
    children = Field()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-04
    • 2019-11-11
    • 2012-08-28
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多