2013-02-20 16 views
13

Provengo da uno sfondo C# in cui la lingua ha alcune funzioni integrate di "protezione dello sviluppatore". Capisco che Python prende l'approccio "siamo tutti adulti qui" e attribuisce la responsabilità allo sviluppatore di codificarlo attentamente e con attenzione.Classi astratte di Python - come scoraggiare l'istanza?

Detto questo, Python suggerisce convenzioni come un trattino di sottolineatura principale per variabili di istanze private. La mia domanda è, c'è una particolare convenzione per marcare una classe come astratta oltre a specificarla solo nelle docstring? Non ho visto nulla in particolare nella guida di stile Python che menzioni le convenzioni di denominazione per le classi astratte.

posso pensare di 3 opzioni finora, ma non sono sicuro se sono buone idee:

  1. specificarlo nella docstring di sopra della classe (potrebbe essere trascurato)
  2. Utilizzare un leader sottolineatura nel nome della classe (non sicuro se questo è universalmente compreso)
  3. Creare un metodo def __init__(self): nella classe astratta che genera un errore (non è sicuro se questo influisce negativamente sull'ereditarietà, ad esempio se si desidera chiamare un costruttore di base)

Una di queste è una buona opzione o ce n'è una migliore? Voglio solo assicurarmi che gli altri sviluppatori sappiano che è astratto e quindi se tentano di istanziarlo devono accettare la responsabilità di qualsiasi comportamento strano.

+0

Se sei preoccupato per qualcuno che chiama 'super .__ init__', o vuoi che faccia qualcosa ma non sia * direttamente * istantaneo, potresti avere qualcosa di simile in' Base .__ init__': 'se type (self) è Base: raise NotImplementedError ('Base .__ init __(): abstract class') ' –

risposta

21

Se si utilizza Python 2.6 o versione successiva, è possibile utilizzare il modulo Abstract Base Class dalla libreria standard se si desidera applicare l'astrattezza.Ecco un esempio:

from abc import ABCMeta, abstractmethod 

class SomeAbstractClass(object): 
    __metaclass__ = ABCMeta 

    @abstractmethod 
    def this_method_must_be_overridden(self): 
     return "But it can have an implementation (callable via super)." 

class ConcreteSubclass(SomeAbstractClass): 
    def this_method_must_be_overridden(self): 
     s = super(ConcreteSubclass, self).this_method_must_be_overridden() 
     return s.replace("can", "does").replace(" (callable via super)", "") 

uscita:

>>> a = SomeAbstractClass() 
Traceback (most recent call last): 
    File "<pyshell#13>", line 1, in <module> 
    a = SomeAbstractClass() 
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract 
methods this_method_must_be_overridden 
>>> c = ConcreteSubclass() 
>>> c.this_method_must_be_overridden() 
'But it does have an implementation.' 
5

Sulla base dell'ultima frase, risponderei alla risposta "documentalo". Chiunque usi una classe in un modo che la documentazione dice di non accettare deve assumersi la responsabilità di qualsiasi comportamento strano.

C'è un meccanismo abstract base class in Python, ma non vedo alcun motivo per usarlo se il tuo unico obiettivo è scoraggiare l'istanza.

+0

+1 perché penso che di solito sia la strada giusta da percorrere. Tuttavia ho dovuto dare la risposta a @Blckknght perché era un po 'più esplicito su come avere un'implementazione sulla base che può essere chiamata solo da un sottotipo. Questo potrebbe essere utile specialmente sui costruttori in cui si potrebbe desiderare qualche logica condivisa sul metodo base __init__'. –

2

Crea la tua classe "astratta" e utilizza return NotImplemented o raise NotImplementedError nei metodi astratti.

Non fermerà le persone che usano la classe, ma con la vera dattilografia, diventerà un problema solo quando si tenta di utilizzare il metodo astratto.

+6

Sii molto cauto con 'return NotImplemented'. Ha un caso d'uso previsto molto specifico. c.f. http://stackoverflow.com/questions/1062096/python-notimplemented- conststant e la descrizione ['NotImplemented' nei documenti] (http://docs.python.org/2/library/constants.html#NotImplemented). Normalmente vorrai 'raise NotImplementedError()'. –

2

Ho appena chiamato le mie classi astratte con il prefisso 'Abstract'. Per esempio. AbstractDevice, AbstractPacket, ecc.

È facile e al punto che arriva. Se gli altri scelgono di andare avanti e creare un'istanza e/o utilizzare una classe che inizia con la parola "Abstract", allora o sanno cosa stanno facendo o non c'è speranza per loro comunque.

Chiamarlo così, serve anche da promemoria per me stesso di non impazzire con le gerarchie astratte profonde, perché mettere "Abstract" sul fronte di un sacco di classi sembra stupido anche.

+1

Una convenzione Python più comune è "Base". Le classi "BaseXYZ" in genere non sono pensate per essere istanziabili. (I due casi che mi vengono subito in mente quando viene utilizzato "Base" sono i modelli Django e il server HTTP integrato di Python.) –

+0

@ChrisMorgan e 'basestring' - genera un' TypeError: il tipo di basestring non può essere istanziato' –

+0

@ ChrisMorgan Alcune discussioni correlate qui: http://mail.python.org/pipermail/python-dev/2003-November/039916.html –

0

Per applicare le cose è possibile, ma piuttosto unpythonic. Quando sono arrivato in Python dopo molti anni di programmazione in C++, ho anche provato a fare lo stesso, suppongo, la maggior parte delle persone prova a farlo se ha un'esperienza in più lingue classiche. I metaclassi farebbero il lavoro, ma in ogni caso Python controlla pochissime cose al momento della compilazione. Il controllo verrà comunque eseguito in fase di runtime. Quindi, l'impossibilità di creare una determinata classe è davvero utile se scoperta solo in fase di runtime? In C++ (e anche in C#) non puoi nemmeno compilare il codice creando una classe astratta, e questo è l'intero punto - per scoprire il problema il prima possibile. Se hai metodi astratti, sollevare un'eccezione NotImplementedError sembra essere abbastanza. NB: rilanciando, non restituendo un codice di errore! In Python gli errori di solito non dovrebbero essere silenziosi a meno che non siano silenziati esplicitamente. Documentare. Denominare una classe in un modo che dice che è astratto. È tutto.

La qualità del codice Python è assicurata principalmente con metodi molto diversi da quelli utilizzati nelle lingue con controllo di tipo avanzato in fase di compilazione. Personalmente ritengo che la differenza più importante tra gli script dinamicamente tipizzati e gli altri. Test unitari, analisi della copertura, ecc. Come risultato, la progettazione del codice è molto diversa: tutto viene fatto per non far rispettare le cose, ma per renderle più semplici possibile.

+0

Se fosse unpitonico, allora suppongo che non vedremmo la libreria 'abc' essere introdotto con Python 2.6 :) –

Problemi correlati