Python:Dataclass继承自基类Dataclass,如何将值从基类升级到新类?
如何将基本数据类的值升级为从其继承的值 示例(Python 3.7.2) 打印“你好,亚历克斯” 返回随机“陌生人”(姓名='Bob',气味='OK')” 我如何把陌生人变成朋友Python:Dataclass继承自基类Dataclass,如何将值从基类升级到新类?,python,python-3.7,python-dataclasses,Python,Python 3.7,Python Dataclasses,如何将基本数据类的值升级为从其继承的值 示例(Python 3.7.2) 打印“你好,亚历克斯” 返回随机“陌生人”(姓名='Bob',气味='OK')” 我如何把陌生人变成朋友 Friend(random_stranger) 返回“Friend(name=Person(name='Bob',嗅觉='OK'),嗅觉='good')” 因此,我想得到“Friend(name='Bob',嗅觉='OK')” Friend(random_stranger.name, random_stranger.
Friend(random_stranger)
返回“Friend(name=Person(name='Bob',嗅觉='OK'),嗅觉='good')”
因此,我想得到“Friend(name='Bob',嗅觉='OK')”
Friend(random_stranger.name, random_stranger.smell)
可以,但如何避免复制所有字段
或者可能我不能在继承自数据类的类上使用@dataclass装饰器吗?您可能不希望类本身是一个可变属性,而是使用诸如枚举之类的东西来指示这样的状态。根据需求,您可以考虑以下几种模式之一:
class RelationshipStatus(Enum):
STRANGER = 0
FRIEND = 1
PARTNER = 2
@dataclass
class Person(metaclass=ABCMeta):
full_name: str
smell: str = "good"
status: RelationshipStatus = RelationshipStatus.STRANGER
@dataclass
class GreetablePerson(Person):
nickname: str = ""
@property
def greet_name(self):
if self.status == RelationshipStatus.STRANGER:
return self.full_name
else:
return self.nickname
def say_hi(self):
print(f"Hi {self.greet_name}")
if __name__ == '__main__':
random_stranger = GreetablePerson(full_name="Robert Thirstwilder",
nickname="Bobby")
random_stranger.status = RelationshipStatus.STRANGER
random_stranger.say_hi()
random_stranger.status = RelationshipStatus.FRIEND
random_stranger.say_hi()
您可能还希望以trait/mixin样式实现这一点。与其创建一个GreetablePerson
,不如创建一个类Greetable
,同时将其抽象,并使您的具体类继承这两个类
<>你也可以考虑使用优秀的、更靠得住的、更灵活的软件包。这还将使您能够使用evolve()
函数创建一个新对象:
friend = attr.evolve(random_stranger, status=RelationshipStatus.FRIEND)
您所要求的是由实现的,并且可以使用
@classmethod
关键字直接在python类中实现
只需在基类定义中包含dataclass工厂方法,如下所示:
import dataclasses
@dataclasses.dataclass
class Person:
name: str
smell: str = "good"
@classmethod
def from_instance(cls, instance):
return cls(**dataclasses.asdict(instance))
@dataclasses.dataclass
class Friend(Person):
def say_hi(self):
print(f'Hi {self.name}')
random_stranger = Person(name = 'Bob', smell='OK')
friend = Friend.from_instance(random_stranger)
print(friend.say_hi())
# "Hi Bob"
从该基类继承的任何新数据类现在都可以创建彼此的实例[1],如下所示:
import dataclasses
@dataclasses.dataclass
class Person:
name: str
smell: str = "good"
@classmethod
def from_instance(cls, instance):
return cls(**dataclasses.asdict(instance))
@dataclasses.dataclass
class Friend(Person):
def say_hi(self):
print(f'Hi {self.name}')
random_stranger = Person(name = 'Bob', smell='OK')
friend = Friend.from_instance(random_stranger)
print(friend.say_hi())
# "Hi Bob"
[1] 如果您的子类引入了没有默认值的新字段,您尝试从子类实例创建父类实例,或者您的父类只有init参数,则此操作将不起作用。
vars(陌生人)
为您提供了dataclass实例陌生人
的所有属性的dict。由于DataClass的默认\uuuuu init\uuuuuuuuuuuuuuuo()
方法采用关键字参数,twin\u陌生人=Person(**vars(陌生人))
创建一个新实例,其中包含值的副本。如果您提供了额外的参数,如陌生人\u got\u friend=friend(**vars(陌生人),city='Rome')
,则这也适用于派生类:
也许创建一个方法
def been_friend(self):返回class Person的friend(self.name、self.嗅觉、其他_参数)
?还不熟悉数据类,但您尝试过改变类吗?同学。在为等价而设计的类上运行良好(状态机模式)。@JLPeyret这听起来像是灾难的良方。您不应该使用vars
来获取数据类的属性,dataclass。asdict
更好vars
将只包括init和class变量,如果使用\uuuuu插槽\uuuuuu
存储属性,则这些属性都不支持,因此不起作用。这就是为什么dataclass.asdict
存在的原因。dataclass.asdict
在我的用例中不起作用,因为它递归地转换子数据类(doc:“数据类、dict、列表和元组被递归为”)。我的数据类通常有子数据类,这些子数据类应该保留为数据类。是的,我可以看出它对您的案例不起作用。在Person
中设置metaclass=ABCMeta
的目的是什么?类Person
仍然可以直接实例化。你说得对。为了使其不可实例化,必须添加抽象方法
,然后在尝试实例化时抛出异常。我不知道我是否同意需要一个抽象方法
,我可能认为这是一个遗漏。但python对于类型错误往往是非攻击性的,所以这可能是python哲学的问题。为了清楚起见,我将保留它,并且可能也不会添加抽象方法,除非它代表Person
的通用接口的一部分。在这种情况下,say_hi()
可能是合适的。
from dataclasses import dataclass
@dataclass
class Person:
name: str
smell: str
@dataclass
class Friend(Person):
city: str
def say_hi(self):
print(f'Hi {self.name}')
friend = Friend(name='Alex', smell='good', city='Berlin')
friend.say_hi() # Hi Alex
stranger = Person(name='Bob', smell='OK')
stranger_got_friend = Friend(**vars(stranger), city='Rome')
stranger_got_friend.say_hi() # Hi Bob