为了账号安全,请及时绑定邮箱和手机立即绑定

Python:从基类 Dataclass 继承的数据类,如何将值从基类升级到新类?

Python:从基类 Dataclass 继承的数据类,如何将值从基类升级到新类?

弑天下 2021-11-16 15:19:46
如何将值从基本数据类升级到继承自它的数据类?示例(Python 3.7.2)from dataclasses import dataclass@dataclassclass Person:    name: str     smell: str = "good"    @dataclassclass Friend(Person):    # ... more fields    def say_hi(self):                print(f'Hi {self.name}')friend = Friend(name='Alex')f1.say_hi()打印“嗨亚历克斯”random_stranger = Person(name = 'Bob', smell='OK')返回 random_stranger “人(姓名 ='鲍勃',气味 ='OK')”如何将 random_stranger 变成朋友?Friend(random_stranger)返回“朋友(姓名=人(姓名='鲍勃',气味='OK'),气味='好')”结果我想得到“朋友(姓名=“鲍勃”,气味=“OK”)”。Friend(random_stranger.name, random_stranger.smell)有效,但如何避免必须复制所有字段?或者我是否可能无法在从数据类继承的类上使用 @dataclass 装饰器?
查看完整描述

3 回答

?
达令说

TA贡献1821条经验 获得超6个赞

您所要求的是由工厂方法模式实现的,并且可以使用@classmethod关键字直接在 python 类中实现。


只需在基类定义中包含一个数据类工厂方法,如下所示:


import dataclasses


@dataclasses.dataclass

class Person:

    name: str

    smell: str = "good"


    @classmethod

    def from_instance(cls, instance):

        return cls(**dataclasses.asdict(instance))

任何从这个基类继承的新数据类现在都可以像这样创建彼此的实例[1]:


@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-only 参数,则它将不起作用。


查看完整回答
反对 回复 2021-11-16
?
慕沐林林

TA贡献2016条经验 获得超9个赞

您可能不希望class自身成为可变属性,而是使用诸如枚举之类的东西来指示诸如此类的状态。根据要求,您可以考虑以下几种模式之一:


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,也是抽象的,并使您的具体类继承这两者。


您还可以考虑使用出色的、反向移植的、更灵活的attrs软件包。这也将使您能够使用以下evolve()功能创建一个新对象:


friend = attr.evolve(random_stranger, status=RelationshipStatus.FRIEND)


查看完整回答
反对 回复 2021-11-16
?
Helenr

TA贡献1780条经验 获得超3个赞

vars(stranger)为您提供数据类实例的所有属性的字典stranger。由于__init__()dataclasses的默认方法采用关键字参数,twin_stranger = Person(**vars(stranger))因此使用值的副本创建一个新实例。如果您提供额外的参数,这也适用于派生类stranger_got_friend = Friend(**vars(stranger), city='Rome'):


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


查看完整回答
反对 回复 2021-11-16
  • 3 回答
  • 0 关注
  • 427 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信