¿Es para recordarse a sí mismo y a su equipo que deben implementar la clase correctamente? No consigo el uso completo de una clase abstracta como esta:
class RectangularRoom(object):
def __init__(self, width, height):
raise NotImplementedError
def cleanTileAtPosition(self, pos):
raise NotImplementedError
def isTileCleaned(self, m, n):
raise NotImplementedError
Respuestas:
Como dice la documentación [docs] ,
Tenga en cuenta que, aunque el caso de uso principal declarado, este error es la indicación de métodos abstractos que deben implementarse en clases heredadas, puede usarlo de la forma que desee, como para indicar un
TODO
marcador.fuente
abc
(ver mi respuesta ).abstractmethod
y deja que subaNotImplementedError
. Esto prohíbesuper().method()
en implementaciones demethod
clases derivadas.Como dice Uriel , está destinado a un método en una clase abstracta que debe implementarse en la clase secundaria, pero que también se puede usar para indicar un TODO.
Existe una alternativa para el primer caso de uso: clases base abstractas . Aquellos ayudan a crear clases abstractas.
Aquí hay un ejemplo de Python 3:
class C(abc.ABC): @abstractmethod def my_abstract_method(self, ...): ...
Al crear una instancia
C
, obtendrá un error porquemy_abstract_method
es abstracto. Necesitas implementarlo en una clase secundaria.TypeError: Can't instantiate abstract class C with abstract methods my_abstract_method
Subclase
C
e implementomy_abstract_method
.class D(C): def my_abstract_method(self, ...): ...
Ahora puedes crear una instancia
D
.C.my_abstract_method
no tiene que estar vacío. Se puede llamarD
usandosuper()
.Una ventaja de esto
NotImplementedError
es que obtiene un explícitoException
en el momento de la instanciación, no en el momento de la llamada al método.fuente
from abc import ABCMeta, abstractmethod
y define tu ABC con__metaclass__ = ABCMeta
. Documentos: docs.python.org/2/library/abc.htmlConsidere si en cambio fue:
class RectangularRoom(object): def __init__(self, width, height): pass def cleanTileAtPosition(self, pos): pass def isTileCleaned(self, m, n): pass
y usted subclasifica y se olvida de decirle cómo
isTileCleaned()
o, quizás más probablemente, escribe comoisTileCLeaned()
. Luego, en su código, obtendrá unNone
cuando lo llame.None
una salida válida? Quién sabe.raise NotImplmentedError
fuerzas que ponerlo en práctica, ya que será una excepción cuando intenta ejecutar hasta que lo haga. Esto elimina muchos errores silenciosos. Es similar a la razón por la que una excepción desnuda casi nunca es una buena idea : porque la gente comete errores y esto asegura que no se barren debajo de la alfombra.Nota: Usar una clase base abstracta, como han mencionado otras respuestas, es mejor aún, ya que entonces los errores se cargan por adelantado y el programa no se ejecutará hasta que los implemente (con NotImplementedError, solo lanzará una excepción si realmente se llama).
fuente
También se podría hacer un método
raise NotImplementedError()
dentro del hijo de un método de@abstractmethod
clase base decorado.Imagine escribir un script de control para una familia de módulos de medición (dispositivos físicos). La funcionalidad de cada módulo se define de manera estricta, implementando solo una función dedicada: una podría ser una matriz de relés, otra un DAC o ADC multicanal, otra un amperímetro, etc.
Gran parte de los comandos de bajo nivel en uso se compartirían entre los módulos, por ejemplo, para leer sus números de identificación o enviarles un comando. Veamos qué tenemos en este punto:
Clase base
from abc import ABC, abstractmethod #< we'll make use of these later class Generic(ABC): ''' Base class for all measurement modules. ''' # Shared functions def __init__(self): # do what you must... def _read_ID(self): # same for all the modules def _send_command(self, value): # same for all the modules
Verbos compartidos
Entonces nos damos cuenta de que gran parte de los verbos de comando específicos del módulo y, por lo tanto, la lógica de sus interfaces también se comparte. Aquí hay 3 verbos diferentes cuyo significado se explica por sí mismo considerando una serie de módulos de destino.
get(channel)
relé: activa el estado de encendido / apagado del relé
channel
DAC: activa el voltaje de salida
channel
ADC: activa el voltaje de entrada
channel
enable(channel)
relé: habilita el uso del relé en
channel
DAC: habilita el uso del canal de salida en
channel
ADC: habilita el uso del canal de entrada en
channel
set(channel)
relé: enciende
channel
/ apaga el reléDAC: configure el voltaje de salida en
channel
ADC: hmm ... no se me ocurre nada lógico .
Los verbos compartidos se convierten en verbos obligatorios
Yo diría que hay un caso sólido para que los verbos anteriores se compartan entre los módulos, ya que vimos que su significado es evidente para cada uno de ellos. Continuaría escribiendo mi clase base
Generic
así:class Generic(ABC): # ...continued @abstractmethod def get(self, channel): pass @abstractmethod def enable(self, channel): pass @abstractmethod def set(self, channel): pass
Subclases
Ahora sabemos que todas nuestras subclases tendrán que definir estos métodos. Veamos cómo se vería para el módulo ADC:
class ADC(Generic): def __init__(self): super().__init__() #< applies to all modules # more init code specific to the ADC module def get(self, channel): # returns the input voltage measured on the given 'channel' def enable(self, channel): # enables accessing the given 'channel'
Puede que ahora te estés preguntando:
Tienes razón: no implementar no
set
es una opción, ya que Python dispararía el siguiente error cuando intentas crear una instancia de tu objeto ADC.TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'
Entonces debes implementar algo, porque creamos
set
un verbo forzado (también conocido como '@abstractmethod'), que es compartido por otros dos módulos pero, al mismo tiempo, tampoco debes implementar nada, yaset
que no tiene sentido para este módulo en particular.NotImplementedError al rescate
Al completar la clase ADC de esta manera:
class ADC(Generic): # ...continued def set(self, channel): raise NotImplementedError("Can't use 'set' on an ADC!")
Estás haciendo tres cosas muy buenas a la vez:
fuente
Es posible que desee utilizar el
@property
decorador,>>> class Foo(): ... @property ... def todo(self): ... raise NotImplementedError("To be implemented") ... >>> f = Foo() >>> f.todo Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in todo NotImplementedError: To be implemented
fuente