Come creare proprietà astratte nelle classi astratte di Python


118

Nel codice seguente creo una classe astratta di base Base. Voglio che tutte le classi che ereditano da Baseforniscano la nameproprietà, quindi ho reso questa proprietà un file @abstractmethod.

Quindi ho creato una sottoclasse di Base, called Base_1, che ha lo scopo di fornire alcune funzionalità, ma rimane comunque astratta. Non ci sono nameproprietà in Base_1, ma ciononostante Python installa un oggetto di quella classe senza errori. Come si creano proprietà astratte?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  

Gotcha: se dimentichi di usare decorator @propertyin class C, nametornerà a un metodo.
kevinarpe

Risposte:


117

A partire da Python 3.3 è stato corretto un bug che significava che il property()decoratore ora è correttamente identificato come astratto quando applicato a un metodo astratto.

Nota: l'ordine è importante, devi usarlo @propertyprima@abstractmethod

Python 3.3 e versioni successive: ( documenti python ):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2: ( documenti python )

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...

1
@ James Come renderlo compatibile anche con Python 2 e?
himanshu219

@ James in realtà intendevo per entrambi, ma non importa, ho pubblicato una risposta basata sulla tua soluzione
himanshu219

45

Fino a Python 3.3 , non è possibile annidare @abstractmethode @property.

Utilizzare @abstractpropertyper creare proprietà astratte ( documenti ).

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

Il codice ora solleva l'eccezione corretta:

Traceback (la chiamata più recente per ultima):
  File "foo.py", riga 36, ​​in 
    b1 = Base_1 ('abc')  
TypeError: impossibile istanziare la classe astratta Base_1 con il nome di metodi astratti

42
in realtà questa risposta è sbagliata per i pitoni più giovani: dal 3.3, @abstractpropertyè deprecata a favore di una combinazione come quella di OP.
pecore volanti



quindi fino al 3.3, soloraise NotImplementedError
Sławomir Lenart

possiamo ereditare dall'oggetto e continuare a utilizzare le annotazioni ABC? Funzionerà come mostrato nell'esempio?
santhosh kumar

3

Basato sulla risposta di James sopra

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

e usalo come decoratore

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.