Cómo crear propiedades abstractas en clases abstractas de Python

118

En el siguiente código, creo una clase abstracta base Base. Quiero que todas las clases que heredan Baseproporcionen la namepropiedad, así que hice esta propiedad como un @abstractmethod.

Luego creé una subclase de Base, llamada Base_1, que está destinada a proporcionar alguna funcionalidad, pero aún permanece abstracta. No hay ninguna namepropiedad en Base_1, pero, sin embargo, Python instala un objeto de esa clase sin error. ¿Cómo se crean propiedades abstractas?

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')  
Boris Gorelik
fuente
Entendido: si olvidas usar el decorador @propertyen class C, namevolverá a un método.
kevinarpe

Respuestas:

117

Desde Python 3.3 se corrigió un error, lo que significa que el property()decorador ahora se identifica correctamente como abstracto cuando se aplica a un método abstracto.

Nota: el pedido es importante, debes usarlo @propertyantes@abstractmethod

Python 3.3+: ( documentos de Python ):

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

Python 2: ( documentos de Python )

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...
James
fuente
1
@James ¿Cómo hacerlo compatible con Python 2 y también?
himanshu219
@James en realidad quise decir para ambos, pero no importa
publiqué
45

Hasta Python 3.3 , no puede anidar @abstractmethody @property.

Úselo @abstractpropertypara crear propiedades abstractas ( documentos ).

from abc import ABCMeta, abstractmethod, abstractproperty

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

El código ahora genera la excepción correcta:

Rastreo (llamadas recientes más última):
  Archivo "foo.py", línea 36, ​​en 
    b1 = Base_1 ('abc')  
TypeError: no se puede crear una instancia de la clase abstracta Base_1 con el nombre de métodos abstractos
codeape
fuente
42
en realidad, esta respuesta es incorrecta para las pitones más jóvenes: desde 3.3, @abstractpropertyestá en desuso a favor de una combinación como OP.
oveja voladora
así que hasta el 3.3, soloraise NotImplementedError
Sławomir Lenart
¿Podemos heredar de un objeto y seguir usando las anotaciones ABC? ¿Funcionará como se muestra en el ejemplo?
santhosh kumar
3

Basado en la respuesta de James anterior

def compatibleabstractproperty(func):

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

y úsalo como decorador

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()
himanshu219
fuente