Implementacja klasy abstrakcyjnej a dostęp do jej unikalnych atrybutów

0

Cześć,

w jednym z kodów widziałem minej więcej takie podejście jak poniżej. Jest klasa abstrakcyjna AbstractClass. Klasy które z niej dziedziczą mają atrybuty, które nie były zdefiniowane w klasie bazowej. Dalej mamy funkcję get_class, która w zależności od parametru (albo czegokolwiek tam), zwraca którąś z klas, które implementują klasę abstrakcyjną, ale określone jest, że zwraca AbstractClass. No i w ostatniej linii odnosimy się do atrybutu klasa2.y, którego klasa AbstractClass, jak i ImplClass1 nie posiadają.

No i zakładając, że programista wie, że w tym miejscu zawsze będzie zwrócona ImplClass2, to czy takie podejście jest OK? Czy nie powinno być tak, że obiekt zwrócony z funkcji get_class będzimy traktować już tylko w kontekście metod które są zdefiniowane w AbstractClass?

Jak ewentualnie podchodzić do sytuacji, że jednak chcielibyśmy odnosić się do atrybutów zdefiniowanych poza klasą AbstractClass?

from dataclasses import dataclass
from abc import ABC

@dataclass
class AbstractClass(ABC):
    def print(self):
        pass

@dataclass
class ImplClass1(AbstractClass):
    x:int
    def print(self):
        print(self.x)

@dataclass
class ImplClass2(AbstractClass):
    x:int
    y:int
    def print(self):
        print(f'{self.x} {self.y}')

def get_class(nr:int)->AbstractClass:
    if nr == 1:
        return ImplClass1(1)
    return ImplClass2(2,3)

klasa2 = get_class(2)
print(klasa2.y)
0

Nie rozumiem pytania. Czy to nie jest właśnie to, co zrobiłeś w 28. linijce? y nie jest atrybutem AbstractClass, a zadziałało jak (chyba?) chciałeś.

0

Zadziałało, ale czy są to dobre praktyki programistyczne?
Zdaje się że w języzykach statycznie typowanych kompliator zwróciłby błąd w takim przypadku?

1

Czy dobre — ja bym powiedział, że nie, bo… to trochę zabija ideę tworzenia czegoś przez klasę abstrakcyjną, nie?

Przy czym ja, tak prawdę powiedziawszy, nie widzę powodu wciskania klas abstrakcyjnych, interfejsów, itd., do języka dynamicznego z duck typingiem, takim jak Python. Jeśli już, to po to, żeby obiecać, że ta czy inna instancja ma na pewno jakiś-tam podzbiór metod/atrybutów… ale to, moim zdaniem, mocno karkołomna konstrukcja na to.

Więc bym się zastanowił, po co Ci w ogóle to dziedziczenie, i po co Ci w ogóle klasa abstrakcyjna jest. Co Ci to mówi, co Ci to daje. I potem, na podstawie tego, zdecydował, czy ta 28. linijka łamie ten kontrakt, czy nie.

EDYCJA: rzuć sobie okiem na to → https://mypy.readthedocs.io/en/stable/protocols.html#protocol-types ← i się zastanów, czy może to Ci lepiej nie pasuje do tego, co chcesz zrobić.

0

Brakuje:

@dataclass
class AbstractClass(ABC):
    @abstractmethod
    def print(self):
        pass

Teraz nie stworzysz instancji :D

0

Jak wspominałem, widziałem takie podejście w kodzie którego nie byłem autorem. Ta klasa abstrakcyjna była po to, jak zrozumiałem, żeby zapewnić implementację kluczowej metody klasy.

Generalnie to nie jestem programistą, coś sobie nieraz amatorsko zrobię i w pracy nieraz trzeba coś zaprogramować, ale kod raczej nie jest moim docelowym produktem. A jako że wcześniej liznąłem i Javę i C++, to pojawiają mi się nieraz takie pytania i wątpliwości przy Pythonie.

Dzięki za komentarze. Wydaje się, że potwierdzają moje przemyślenia na ten temat.

0

Generalnie słabo. Po to używasz abstrakcji, żeby w danym miejscu polegać tylko na niej a nie na szczegółach implementacji. W tym wypadku masz abstrakcję, która nie działa: get_class zwraca coś, co może wywołać print i tyle. Z drugiej strony ciężko coś dopowiedzieć, bo bez kontekstu i potrzeby bardzo ciężko coś wywróżyć

1 użytkowników online, w tym zalogowanych: 0, gości: 1