简体   繁体   English

在 class 属性定义中使用相同的 class

[英]Using the same class in the class attribute definition

How can I make something like this?我怎么能做这样的事情?

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)

Renders:渲染:

NameError: name 'TaskForm' is not defined

The error occurs on the line defining the children attribute.错误发生在定义children属性的行上。

I use class attributes in the __new__ function of the metaclass and I can not move it to __init__ function of current class. I use class attributes in the __new__ function of the metaclass and I can not move it to __init__ function of current class.

My metaclass (Like in django framework):我的元类(如 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

Inside a class statement body, the class that is being declared not yet exists - and therefore it can't be referenced like in your example.class语句正文中,正在声明的 class 尚不存在 - 因此不能像您的示例中那样引用它。 Django and the original use of type annotations in Python PEP 484 work around this by allowing you to put the class name as a string instead - but this does not help when you want a plain reference to the class. Django and the original use of type annotations in Python PEP 484 work around this by allowing you to put the class name as a string instead - but this does not help when you want a plain reference to the class.

What there is in Python is the "descriptor protocol" - sone few methods that if implemented in classes of things that are intended to be used as fields - just like in your case, are called automatically by Python. Python 中的内容是“描述符协议” - 如果在旨在用作字段的事物类中实现的少数方法 - 就像在您的情况下一样,由 Python 自动调用。 __get__ , __set__ and __delete__ methods are called on attribute access, and __set_name__ is called when the class is created, and passed the owner class. __get____set____delete__方法在属性访问时被调用,而__set_name__在创建 class 并传递所有者 class 时被调用。 You can rely on that for your Field class.您可以将其用于您的Field class。 No changes are needed for your metaclass.您的元类不需要更改。 (just keep in mind that __set_name__ is called at the end of execution of type.__new__ , before a metaclass __init__ runs.): (请记住,在执行type.__new__ __set_name__时调用 __set_name__ ,在元类__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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM