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.
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):
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. 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. __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. You can rely on that for your 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.):
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()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.