Python: Dataclass, который наследуется от базового Dataclass, как мне обновить значение с базового до нового класса? - PullRequest
0 голосов
/ 22 февраля 2019

Как я могу обновить значения из базового класса данных до того, который унаследован от него?

Пример (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()

печатает "Привет Алекс"

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

возврат для random_stranger "Персона (имя = 'Bob', запах = 'OK')"

Как превратить random_stranger в друга?

Friend(random_stranger)

возврат«Друг (имя = человек (имя =« Боб », запах =« ОК »), запах =« хорошо »)«

Я бы хотел получить «Друг (имя =« Боб », запах ='OK') "в результате.

Friend(random_stranger.name, random_stranger.smell)

работает, но как мне избежать необходимости копировать все поля?

Или возможно, что я не могу использовать @dataclassдекоратор классов, которые наследуются от классов данных?

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

Вы, вероятно, не хотите, чтобы само class было изменяемым свойством, и вместо этого используйте что-то, например, enum, для обозначения такого состояния, как это.В зависимости от требований вы можете рассмотреть один из нескольких шаблонов:

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()

Возможно, вы захотите также реализовать это в стиле черта / смешивание.Вместо создания GreetablePerson, вместо этого сделайте класс Greetable, также абстрактный, и сделайте так, чтобы ваш конкретный класс наследовал оба из них.

Вы также можете рассмотреть возможность использования превосходного бэкпорта, гораздо более гибкого attrs пакет.Это также позволит вам создать новый объект с функцией evolve():

friend = attr.evolve(random_stranger, status=RelationshipStatus.FRIEND)
0 голосов
/ 08 марта 2019

То, что вы запрашиваете, реализуется с помощью шаблона фабричного метода и может быть прямо реализовано в классах Python с помощью ключевого слова @classmethod.

Просто включите фабричный метод класса данныхметод в определении вашего базового класса, например:

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]: Это не будет работать, если ваши дочерние классы вводят новые поля без значений по умолчанию или вы пытаетесь создать экземпляры родительских классов из экземпляров дочерних классов.

0 голосов
/ 22 февраля 2019

Быстрый взлом для этого:

random_stranger = Person(name = 'Bob', smell='OK')
friend = Friend(**random_stranger.__dict__)

Это возьмет все атрибуты random_stranger и передаст их конструктору Friend.

...