Cómo crear propiedades abstractas en clases abstractas de python
⏰ 6 minutos de lectura
Boris Gorelik
En el siguiente código, creo una clase abstracta base Base. Quiero todas las clases que heredan de Base para proporcionar el name propiedad, así que hice de esta propiedad una @abstractmethod.
Luego creé una subclase de Basellamó Base_1, que está destinado a proporcionar alguna funcionalidad, pero sigue siendo abstracto. No hay name propiedad 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')
Gotcha: si olvidas usar decorador @property en class C, name volverá a un método.
– Kevinarpe
2 de noviembre de 2014 a las 5:35
Jaime
Ya que Pitón 3.3 se solucionó un error que significaba que property() decorador ahora se identifica correctamente como abstracto cuando se aplica a un método abstracto.
Nota: el orden importa, tienes que usar @property arriba @abstractmethod
from abc import ABC, abstractproperty
class C(ABC):
@abstractproperty
def my_abstract_property(self):
...
@James ¿Cómo hacerlo compatible con Python 2 y también?
– himanshu219
12 de julio de 2019 a las 6:52
@James en realidad quise decir para ambos, pero no importa, publiqué una respuesta basada en su solución
– himanshu219
12 de julio de 2019 a las 11:55
no creo que python verifique que la implementación realmente tenga un decorador @property, solo verifica que un método con el nombre my_abstract_property es creado.
– ierdna
9 de agosto de 2021 a las 16:33
@James, ¿funciona esto con functools.cached_property?
– lmiguelvargasf
20 de marzo a las 18:10
no lo entiendo OP está preguntando acerca de tener una propiedad de una clase base llamada name, que debe ser implementado por todas las clases secundarias, pero las respuestas aquí implementan funciones abstractas. Incluso el abc Los documentos muestran solo funciones abstractas. ¿No hay una manera de hacer que las propiedades/variables de clase sean abstractas de manera que las clases secundarias tengan que implementarlas? lo necesito para tener id variable que todas las clases secundarias deben implementar.
– Navegación
7 de mayo a las 11:19
código
Hasta que Pitón 3.3no puedes anidar @abstractmethod y @property.
Usar @abstractproperty para 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:
Traceback (most recent call last):
File "foo.py", line 36, in
b1 = Base_1('abc')
TypeError: Can't instantiate abstract class Base_1 with abstract methods name
en realidad, esta respuesta es incorrecta para las pitones más jóvenes: desde 3.3, @abstractproperty está en desuso a favor de una combinación como OP.
Utilizando el @property decorador en la clase abstracta (como se recomienda en la respuesta de James) funciona si desea que los atributos de nivel de instancia requeridos también usen el decorador de propiedades.
Si no desea utilizar el decorador de propiedades, puede utilizar super(). Terminé usando algo como el __post_init__() de las clases de datos y obtiene la funcionalidad deseada para los atributos de nivel de instancia:
import abc
from typing import List
class Abstract(abc.ABC):
"""An ABC with required attributes.
Attributes:
attr0
attr1
"""
@abc.abstractmethod
def __init__(self):
"""Forces you to implement __init__ in 'Concrete'.
Make sure to call __post_init__() from inside 'Concrete'."""
def __post_init__(self):
self._has_required_attributes()
# You can also type check here if you want.
def _has_required_attributes(self):
req_attrs: List[str] = ['attr0', 'attr1']
for attr in req_attrs:
if not hasattr(self, attr):
raise AttributeError(f"Missing attribute: '{attr}'")
class Concrete(Abstract):
def __init__(self, attr0, attr1):
self.attr0 = attr0
self.attr1 = attr1
self.attr2 = "some value" # not required
super().__post_init__() # Enforces the attribute requirement.
Gers
En pitón 3.6+, también puede anotar una variable sin proporcionar un valor predeterminado. Creo que esta es una forma más concisa de hacerlo abstracto.
class Base():
name: str
def print_name(self):
print(self.name) # will raise an Attribute error at runtime if `name` isn't defined in subclass
class Base_1(Base):
name = "base one"
también se puede usar para forzarlo a inicializar la variable en el __new__ o __init__ métodos
Como otro ejemplo, el siguiente código fallará cuando intente inicializar el Base_1 clase
class Base():
name: str
def __init__(self):
self.print_name()
class Base_1(Base):
_nemo = "base one"
b = Base_1()
AttributeError: 'Base_1' object has no attribute 'name'
No funcionó para mí. Pitón 3.9.6. ¿Quizás te perdiste algo?
– Navegación
7 de mayo a las 11:15
bueno, algo que podría no quedar claro en mi respuesta es que solo obtendrá el error cuando intente acceder al atributo faltante. para que puedas comprobarlo fácilmente en el __init__ método por ejemplo. Actualizaré mi respuesta para demostrar eso. (por cierto, lo probé con Python 3.8.6)
– Gers
8 de mayo a las 13:44
Tal vez esta respuesta sea útil, pero no es lo que pregunta la pregunta OP. Está definiendo un atributo, no una propiedad.
– Colin D. Bennett
28 de junio a las 17:32
No funcionó para mí. Pitón 3.9.6. ¿Quizás te perdiste algo?
– Navegación
7 de mayo a las 11:15
bueno, algo que podría no quedar claro en mi respuesta es que solo obtendrá el error cuando intente acceder al atributo faltante. para que puedas comprobarlo fácilmente en el __init__ método por ejemplo. Actualizaré mi respuesta para demostrar eso. (por cierto, lo probé con Python 3.8.6)
– Gers
8 de mayo a las 13:44
Tal vez esta respuesta sea útil, pero no es lo que pregunta la pregunta OP. Está definiendo un atributo, no una propiedad.
– Colin D. Bennett
28 de junio a las 17:32
¿Ha sido útil esta solución?
Tu feedback nos ayuda a saber si la solución es correcta y está funcionando. De esta manera podemos revisar y corregir el contenido.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información
Gotcha: si olvidas usar decorador
@property
enclass C
,name
volverá a un método.– Kevinarpe
2 de noviembre de 2014 a las 5:35