使用 metaclasses 继承 class 字典
Inheriting the class dictionary with metaclasses
我正在使用 metaclass:
设置 class 属性 fields
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
clsdict['fields'] = {k: v
for k, v in clsdict.items()
if <my_condition>}
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
class MyBaseClass(metaclass=MyMeta):
fields = {}
以下实例化导致预期结果:
class SubClass(MyBaseClass):
param1 = 1 # meets <my_condition>
>>> SubClass.fields
{param1: 1}
但是如果我现在subclass SubClass
, fields
是空的:
class SubSubClass(SubClass):
pass
>>> SubSubClass.fields
{}
我如何能够更新继承层次结构中所有 classes 的 classdict,以便 fields
变量将从基础 classes 更新?
您需要以某种方式保留超类的 fields
,例如迭代 "bases" 并使用它们的 fields
作为起点:
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
if 'fields' not in clsdict:
clsdict['fields'] = {}
# Initialize "fields" from base classes
for base in bases:
try:
clsdict['fields'].update(base.fields)
except AttributeError:
pass
# Fill in new fields (I included a "trivial" condition here, just use yours instead.)
clsdict['fields'].update({k: v for k, v in clsdict.items() if k.startswith('param')})
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
它适用于 SubClass
和 SubSubClass
:
>>> SubClass.fields
{'param1': 1}
>>> SubSubClass.fields
{'param1': 1}
我建议将 fields
变成一个 属性 描述符,它从父 classes 获取 _fields
的所有内容。这样您还可以更轻松地自定义出现名称冲突等情况时发生的情况。
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
# change fields to _fields
clsdict['_fields'] = {k: v
for k, v in clsdict.items()
if <my_condition>}
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
@property
def fields(cls):
# reversed makes most recent key value override parent values
return {k:v
for c in reversed(cls.__mro__)
for k,v in getattr(c, '_fields', {}).items() }
用法:
class MyBaseClass(metaclass=MyMeta):
fields = {}
class SubClass(MyBaseClass):
param1 = 1
>>> SubClass.fields
{param1: 1}
class SubSubClass(SubClass):
pass
>>> SubSubClass.fields
{param1: 1} # success
现在,SomeChildClass.fields
的用法总是指元class 属性。 getattr
的第三个参数允许没有 _fields
属性的 classes(例如 object
)静默失败。
使用描述符还具有防止子 class 意外覆盖 fields
属性的优点:
>>> SubSubClass.fields = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
如果需要,您还可以创建一个 setter,然后在 __init__
方法中使用它(即返回使用 fields
而不是 _fields
)因此 class 的其余部分与实现无关:
@fields.setter
def fields(cls, mapping):
try:
cls._fields.update(**mapping)
except AttributeError:
cls._fields = dict(**mapping)
我正在使用 metaclass:
设置 class 属性fields
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
clsdict['fields'] = {k: v
for k, v in clsdict.items()
if <my_condition>}
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
class MyBaseClass(metaclass=MyMeta):
fields = {}
以下实例化导致预期结果:
class SubClass(MyBaseClass):
param1 = 1 # meets <my_condition>
>>> SubClass.fields
{param1: 1}
但是如果我现在subclass SubClass
, fields
是空的:
class SubSubClass(SubClass):
pass
>>> SubSubClass.fields
{}
我如何能够更新继承层次结构中所有 classes 的 classdict,以便 fields
变量将从基础 classes 更新?
您需要以某种方式保留超类的 fields
,例如迭代 "bases" 并使用它们的 fields
作为起点:
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
if 'fields' not in clsdict:
clsdict['fields'] = {}
# Initialize "fields" from base classes
for base in bases:
try:
clsdict['fields'].update(base.fields)
except AttributeError:
pass
# Fill in new fields (I included a "trivial" condition here, just use yours instead.)
clsdict['fields'].update({k: v for k, v in clsdict.items() if k.startswith('param')})
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
它适用于 SubClass
和 SubSubClass
:
>>> SubClass.fields
{'param1': 1}
>>> SubSubClass.fields
{'param1': 1}
我建议将 fields
变成一个 属性 描述符,它从父 classes 获取 _fields
的所有内容。这样您还可以更轻松地自定义出现名称冲突等情况时发生的情况。
class MyMeta(type):
def __new__(mcs, name, bases, clsdict):
# change fields to _fields
clsdict['_fields'] = {k: v
for k, v in clsdict.items()
if <my_condition>}
return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)
@property
def fields(cls):
# reversed makes most recent key value override parent values
return {k:v
for c in reversed(cls.__mro__)
for k,v in getattr(c, '_fields', {}).items() }
用法:
class MyBaseClass(metaclass=MyMeta):
fields = {}
class SubClass(MyBaseClass):
param1 = 1
>>> SubClass.fields
{param1: 1}
class SubSubClass(SubClass):
pass
>>> SubSubClass.fields
{param1: 1} # success
现在,SomeChildClass.fields
的用法总是指元class 属性。 getattr
的第三个参数允许没有 _fields
属性的 classes(例如 object
)静默失败。
使用描述符还具有防止子 class 意外覆盖 fields
属性的优点:
>>> SubSubClass.fields = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
如果需要,您还可以创建一个 setter,然后在 __init__
方法中使用它(即返回使用 fields
而不是 _fields
)因此 class 的其余部分与实现无关:
@fields.setter
def fields(cls, mapping):
try:
cls._fields.update(**mapping)
except AttributeError:
cls._fields = dict(**mapping)