Sono appropriate le classi di base astratte se non utilizzo né '@abstractmethod' né 'register'

0

Ho due classi non correlate che condividono entrambi un determinato comportamento. È appropriato usare una classe base astratta comune per le due classi, anche se una semplice classe base otterrebbe la stessa cosa. Non userò mai register né la mia classe base astratta usa @abstractmethod .

L'unica ragione per cui deriverei la classe base da ABCMeta è di documentare che questa classe non dovrebbe mai essere istanziata direttamente. (Può, tuttavia, essere istanziato, perché non ha metodi astratti)

    
posta TheEspinosa 14.06.2017 - 09:56
fonte

1 risposta

1

In Python, ereditare da classi mixin-like è un approccio ok-ish poiché il linguaggio supporta l'ereditarietà multipla. L'unica avvertenza è il metodo __init__() : tutte le classi da cui si eredita devono avere costruttori compatibili poiché il loro ordine non è noto al momento della definizione della classe.

In pratica, l'utilizzo dell'ereditarietà di solito non è appropriato in quanto la delega può ottenere lo stesso risultato con un mal di testa molto inferiore. Quindi invece di

class Mixin(object):
  def __init__(self, x):
    self.__x = x

  def method(self):
    ...

class A(Mixin):
  def __init__(self, x):
    super().__init__(x)

  ...

class B(Mixin):
  def __init__(self, x):
    super().__init__(x)

  ...

Potremmo dire

class Mixin(object):
  def __init__(self, x):
    self.__x = x

  def method(self):
    ...

class A(Mixin):
  def __init__(self, x):
    self.__mixin = Mixin(x)

  def method(self):
    return self.__mixin.method()

  ...

class B(Mixin):
  def __init__(self, x):
    self.__mixin = Mixin(x)

  def method(self):
    return self.__mixin.method()

  ...

(Lo svantaggio della delega manuale è che le docstring si perdono, ma prevenire in modo affidabile è un po 'non banale a causa delle più sottili complessità del protocollo di risoluzione dei membri dell'oggetto Python.)

Se il mixin non ha né più metodi pubblici né dati associati, è preferibile una funzione libera invece di una classe di mixin:

def common_method(self):
  ...

class A(Mixin):
  def method(self):
    return common_method()

  ...

class B(Mixin):
  def method(self):
    return common_method()

  ...

La delega è inappropriata solo nel caso in cui sia necessario eseguire controlli isinstance(o, Mixin) . Nella maggior parte dei casi questo è un difetto di progettazione e può essere evitato, specialmente se il codice che esegue tale controllo è sotto il tuo controllo. In caso contrario, il modello dell'adattatore dell'oggetto potrebbe essere adatto, che è fondamentalmente uguale all'esempio di delega, ma capovolto:

class RequiredType(object):
  ...

class ActualType(object):
  ...

class Adapter(RequiredType):
  def __init__(self, target):
    self.__target = target

  def method_of_required_type(self):
     return self.__target.method_of_actual_type()

Ora data una funzione needs_required_type(...) , data un'istanza del tipo effettivo potresti chiamarla needs_required_type(Adapter(instance)) . Di solito è preferibile ereditare il tipo richiesto nel tipo effettivo, dal momento che l'ereditarietà espone un sacco di metodi e campi indesiderati. Ciò è particolarmente negativo per tipi predefiniti come str o list . Ho visto casi che hanno reso un'API assolutamente incomprensibile perché una classe ha ereditato da list come un dettaglio di implementazione, il che ha praticamente rovinato ogni pretesa di incapsulamento.

    
risposta data 14.06.2017 - 10:53
fonte

Leggi altre domande sui tag