Python:从基础数据class继承的数据class,如何将一个值从基础数据升级到新的class?

Python: Dataclass that inherits from base Dataclass, how do I upgrade a value from base to the new class?

如何将值从基础数据类升级到继承自它的值?

示例 (Python 3.7.2)

from dataclasses import dataclass

@dataclass
class Person:
    name: str 
    smell: str = "good"    

@dataclass
class Friend(Person):

    # ... more fields

    def say_hi(self):        
        print(f'Hi {self.name}')

friend = Friend(name='Alex')
f1.say_hi()

打印"Hi Alex"

random_stranger = Person(name = 'Bob', smell='OK')

return 对于 random_stranger "Person(name='Bob', smell='OK')"

如何将 random_stranger 变成朋友?

Friend(random_stranger)

returns "Friend(name=Person(name='Bob', smell='OK'), smell='good')"

我想得到 "Friend(name='Bob', smell='OK')" 作为结果。

Friend(random_stranger.name, random_stranger.smell)

有效,但如何避免必须复制所有字段?

或者我可能无法在继承自 data类 的 类 上使用 @dataclass 装饰器?

您可能不希望 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,而是创建 class Greetable,也是抽象的,并让你的具体 class 继承这两者。

您也可以考虑使用优秀的、向后移植的、更加灵活的 attrs 包。这也将使您能够使用 evolve() 函数创建一个新对象:

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

你所要求的是由factory method pattern实现的,并且可以直接使用@classmethod关键字在pythonclasses中实现。

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

import dataclasses

@dataclasses.dataclass
class Person:
    name: str
    smell: str = "good"

    @classmethod
    def from_instance(cls, instance):
        return cls(**dataclasses.asdict(instance))

继承自该基础class的任何新数据class现在可以创建彼此的实例[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] 如果你的 child classes 引入没有默认值的新字段,你尝试创建 parent class 个实例来自 child class 个实例,或者你的 parent class 有 init-only 个参数。

vars(stranger) 给出数据类实例所有属性的字典 stranger。由于 data类 的默认 __init__() 方法采用关键字参数,因此 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