我目前正在尝试Python 3.7中引入的新数据类结构。我目前被困在试图做一些继承的父类。看起来参数的顺序被我当前的方法搞砸了,比如子类中的bool形参在其他形参之前传递。这将导致一个类型错误。
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent('jack snr', 32, ugly=True)
jack_son = Child('jack jnr', 12, school = 'havard', ugly=True)
jack.print_id()
jack_son.print_id()
当我运行这段代码时,我得到这个TypeError:
TypeError: non-default argument 'school' follows default argument
我怎么解决这个问题?
一种可行的解决方法是使用monkey-patch来附加父字段
import dataclasses as dc
def add_args(parent):
def decorator(orig):
"Append parent's fields AFTER orig's fields"
# Aggregate fields
ff = [(f.name, f.type, f) for f in dc.fields(dc.dataclass(orig))]
ff += [(f.name, f.type, f) for f in dc.fields(dc.dataclass(parent))]
new = dc.make_dataclass(orig.__name__, ff)
new.__doc__ = orig.__doc__
return new
return decorator
class Animal:
age: int = 0
@add_args(Animal)
class Dog:
name: str
noise: str = "Woof!"
@add_args(Animal)
class Bird:
name: str
can_fly: bool = True
Dog("Dusty", 2) # --> Dog(name='Dusty', noise=2, age=0)
b = Bird("Donald", False, 40) # --> Bird(name='Donald', can_fly=False, age=40)
也可以预先添加非默认字段,
通过检查f.default是否为dc。失踪,
但这可能太脏了。
虽然猴子补丁缺乏遗传的一些特征,
它仍然可以用于向所有伪子类添加方法。
对于更细粒度的控制,请设置默认值
使用直流。字段(compare=False, repr=True,…)
下面的方法在使用纯python数据类和没有太多样板代码的情况下处理这个问题。
丑陋的:数据类。InitVar[bool]只是作为一个伪字段来帮助我们进行初始化,一旦创建实例就会丢失。而_ugly: bool = field(init=False)是一个实例成员,它不会通过__init__方法初始化,但也可以使用__post_init__方法初始化(你可以在这里找到更多)。
from dataclasses import dataclass, field, InitVar
@dataclass
class Parent:
name: str
age: int
ugly: InitVar[bool]
_ugly: bool = field(init=False)
def __post_init__(self, ugly: bool):
self._ugly = ugly
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
jack = Parent('jack snr', 32, ugly=True)
jack_son = Child('jack jnr', 12, school='havard', ugly=True)
jack.print_id()
jack_son.print_id()
注意,这使得字段ugly成为强制性的,使其成为可选的。你可以在父类上定义一个类方法,其中包含ugly作为可选参数:
from dataclasses import dataclass, field, InitVar
@dataclass
class Parent:
name: str
age: int
ugly: InitVar[bool]
_ugly: bool = field(init=False)
def __post_init__(self, ugly: bool):
self._ugly = ugly
@classmethod
def create(cls, ugly=True, **kwargs):
return cls(ugly=ugly, **kwargs)
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
jack = Parent.create(name='jack snr', age=32, ugly=False)
jack_son = Child.create(name='jack jnr', age=12, school='harvard')
jack.print_id()
jack_son.print_id()
现在您可以使用create(…)类方法作为创建父/子类的工厂方法,并使用默认值ugly。注意,这种方法必须使用命名参数才能工作。