herency - super class self__ init__ python
Evitar que una clase ejemplifique directamente en Python (4)
Tengo una súper clase con un método que llama a otros métodos que solo están definidos en sus subclases. Por eso, cuando creo una instancia de mi súper clase y llamo a su método, no puede encontrar el método y genera un error.
Aquí hay un ejemplo:
class SuperClass(object):
def method_one(self):
value = self.subclass_method()
print value
class SubClassOne(SuperClass):
def subclass_method(self):
return ''subclass 1''
class SubClassTwo(SuperClass):
def subclass_method(self):
return ''nubclass 2''
s1 = SubClassOne()
s1.method_one()
s2 = SubClassTwo()
s2.method_one()
c = SuperClass()
c.method_one()
# Results:
# subclass 1
# nubclass 2
# Traceback (most recent call last):
# File "abst.py", line 28, in <module>
# c.method_one()
# File "abst.py", line 4, in method_one
# value = self.subclass_method()
# AttributeError: ''SuperClass'' object has no attribute ''subclass_method''
Estaba pensando en cambiar el inicio de la superclase y verificar el tipo de objeto, cuando se crea una nueva instancia. Si el objeto pertenece a la superclase, genera un error. Sin embargo, no estoy muy seguro de si es la forma pitónica de hacerlo.
¿Alguna recomendación?
Estás hablando de clases base abstractas, y el lenguaje Python no las admite de forma nativa.
Sin embargo, en la biblioteca estándar, hay un módulo que puedes usar para ayudarte. Echa un vistazo a la documentación abc .
Esto es lo que podría hacer:
class SuperClass(object):
def __init__(self):
if type(self) == SuperClass:
raise Exception("<SuperClass> must be subclassed.")
# assert(type(self) == SuperClass)
class SubClass(SuperClass):
def __init__(self):
SuperClass.__init__(self)
subC = SubClassOne()
supC = SuperClass() # This line should throw an exception
Cuando se ejecuta (se lanza la excepción):
[ 18:32 jon@hozbox ~/so/python ]$ ./preventing-direct-instantiation.py
Traceback (most recent call last):
File "./preventing-direct-instantiation.py", line 15, in <module>
supC = SuperClass()
File "./preventing-direct-instantiation.py", line 7, in __init__
raise Exception("<SuperClass> must be subclassed.")
Exception: <SuperClass> must be subclassed.
Editar (a partir de comentarios):
[ 20:13 jon@hozbox ~/SO/python ]$ cat preventing-direct-instantiation.py
#!/usr/bin/python
class SuperClass(object):
def __init__(self):
if type(self) == SuperClass:
raise Exception("<SuperClass> must be subclassed.")
class SubClassOne(SuperClass):
def __init__(self):
SuperClass.__init__(self)
class SubSubClass(SubClassOne):
def __init__(self):
SubClassOne.__init__(self)
class SubClassTwo(SubClassOne, SuperClass):
def __init__(self):
SubClassOne.__init__(self)
SuperClass.__init__(self)
subC = SubClassOne()
try:
supC = SuperClass()
except Exception, e:
print "FAILED: supC = SuperClass() - %s" % e
else:
print "SUCCESS: supC = SuperClass()"
try:
subSubC = SubSubClass()
except Exception, e:
print "FAILED: subSubC = SubSubClass() - %s" % e
else:
print "SUCCESS: subSubC = SubSubClass()"
try:
subC2 = SubClassTwo()
except Exception, e:
print "FAILED: subC2 = SubClassTwo() - %s" % e
else:
print "SUCCESS: subC2 = SubClassTwo()"
Huellas dactilares:
[ 20:12 jon@hozbox ~/SO/python ]$ ./preventing-direct-instantiation.py
FAILED: supC = SuperClass() - <SuperClass> must be subclassed.
SUCCESS: subSubC = SubSubClass()
SUCCESS: subC2 = SubClassTwo()
Su enfoque es un patrón de marco típico.
Usar __init__ para verificar que type(self) is not SuperClass
es una forma razonable de asegurarse de que SuperClass no haya sido instanciado directamente.
El otro enfoque común es proporcionar métodos de código auxiliar que raise NotImplementedError
cuando se los llama. Eso es más confiable porque también valida que las subclases hayan anulado los métodos esperados.
__new__()
en la clase base y simplemente no podría crear una instancia si es la clase base.
class BaseClass(object):
def __new__(cls, *args, **kwargs):
if cls is BaseClass:
raise TypeError("base class may not be instantiated")
return object.__new__(cls, *args, **kwargs)
Esto separa las preocupaciones un poco mejor que __init__()
en __init__()
, y "falla rápidamente".