给定以下python数据类的层次结构:
@dataclass
class A:
a: str
aa: str
@dataclass
class B(A):
b: str
@dataclass
class C(A):
c: str
@dataclass
class D(B, C):
d: str
是否有任何常规方法允许您在此继承层次结构中插入具有默认值的属性?例如,下面为类D
生成一个Non-default argument(s) follows default argument(s) defined in 'B'
错误
@dataclass
class A:
a: str
aa: str
@dataclass
class B(A):
b: str = 'b' # <-- default value
@dataclass
class C(A):
c: str
@dataclass
class D(B, C):
d: str
我认为一种解决方案是为层次结构中的所有属性提供一个默认值None
,并添加一个__post_init__()
验证,如果任何属性都是None
,则会引发一些异常,但这似乎不正确,而且层次结构中的每个数据类都需要它
Class inheritance in Python 3.7 dataclasses提出了一个解决方案,其中创建继承层次结构的两个独立分支;一个用于默认值,另一个不用于默认值。这里的缺点是层次结构很快就会被很多类弄乱,但是如果没有更好的选项,我想这是最好的了
我想出了一个meta_dataclass
方法,它至少可以解决我的问题。我相信它打破了各种规则,但也许有人可以改进它
它允许meta_dataclasses
层次结构,其中层次结构中的任何属性都可以有默认值。它是由Class inheritance in Python 3.7 dataclasses中提出的解决方案推动的。它有效地创建了两个继承分支,一个带有默认值,一个没有,但是实现这一点所需的额外类的创建被封装在mete_dataclass
decorator中。当前的缺点是meta_dataclass
只能从其他meta_dataclasses
继承
完整代码:
from dataclasses import make_dataclass, MISSING, field
class Dataclass:
pass
class Declarations:
pass
class Definitions:
pass
def meta_dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False):
def wrap(cls):
declaration_bases = []
definition_bases = []
for base in cls.__bases__:
if issubclass(base, Dataclass):
declaration_bases += [c for c in base.__bases__ if issubclass(c, Declarations)]
definition_bases += [c for c in base.__bases__ if issubclass(c, Definitions)]
elif len(cls.__bases__) == 1 and base != object:
raise ValueError(f'meta_dataclasses can only inherit from other meta_dataclasses. '
f'{cls} inherits from {base}')
declaration_bases.append(Declarations)
definition_bases.append(Definitions)
fields = []
if hasattr(cls, '__annotations__'):
for field_name, field_type in cls.__annotations__.items():
f = field(default=cls.__dict__[field_name]) if field_name in cls.__dict__ else field()
fields.append((field_name, field_type, f))
declarations = make_dataclass(cls_name=f'{cls.__name__}_Declarations',
bases=tuple(declaration_bases),
fields=[f for f in fields if isinstance(f[2].default, type(MISSING))],
init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)
definitions = make_dataclass(cls_name=f'{cls.__name__}_Definitions',
bases=tuple(definition_bases),
fields=[f for f in fields if not isinstance(f[2].default, type(MISSING))],
init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)
cls_wrapper = make_dataclass(cls_name=cls.__name__, fields=[], bases=(Dataclass, definitions, declarations),
namespace=cls.__dict__, init=init, repr=repr, eq=eq, order=order,
unsafe_hash=unsafe_hash, frozen=frozen)
return cls_wrapper
if cls is None:
return wrap
else:
return wrap(cls)
示例:
@meta_dataclass
class A:
a: str
aa: str
@meta_dataclass
class B(A):
b: str = 'b'
@meta_dataclass
class C(A):
c: str
@meta_dataclass
class D(B, C):
d: str
>>> help(D)
Help on class D in module types:
class D(__main__.Dataclass, D_Definitions, D_Declarations)
| D(a: str, aa: str, c: str, d: str, b: str = 'b') -> None
|
| D(a: str, aa: str, c: str, d: str, b: str = 'b')
|
| Method resolution order:
| D
| __main__.Dataclass
| D_Definitions
| B_Definitions
| C_Definitions
| A_Definitions
| __main__.Definitions
| D_Declarations
| B_Declarations
| C_Declarations
| A_Declarations
| __main__.Declarations
| builtins.object
插图:
下图说明了@meta_dataclass
装饰器对单个类所做的操作
目前没有回答
相关问题 更多 >
编程相关推荐