Métodos abstractos en Python [duplicate]

9 minutos de lectura

Tengo problemas para usar la herencia con Python. Si bien el concepto parece demasiado fácil para mí en Java, hasta ahora no he podido entenderlo en Python, lo que al menos me sorprende.

Tengo un prototipo que sigue:

class Shape():
   def __init__(self, shape_name):
       self.shape = shape_name

class Rectangle(Shape):
   def __init__(self, name):
       self.shape = name

En el código anterior, ¿cómo puedo crear un método abstracto que deba implementarse para todas las subclases?

  • Por cierto, esto realmente fue una ocurrencia tardía en Python. Las interfaces abstractas y demás se introdujeron recientemente. Dado que python no está compilado, debe dejar clara su intención en la documentación en lugar de en las clases abstractas.

    – Falmarri

    8 de diciembre de 2010 a las 0:05

  • También una muy buena aplicación para pruebas unitarias. Como Python no está compilado, no empiezo a confiar en el código hasta que lo probé.

    – Tecnófilo

    8 de julio de 2021 a las 15:05

avatar de usuario
kevpie

Antes de que se introdujera abc, vería esto con frecuencia.

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


class Specialized(Base):
    def go(self):
        print "Consider me implemented"

  • en mi opinión, esto es más pitónico que la respuesta aceptada, ya que sigue el adagio de Python “aquí todos somos adultos”: si una subclase no implementa este tipo de método abstracto, tendrá que lidiar con las consecuencias

    –Emmett Butler

    24 de septiembre de 2012 a las 15:17

  • @ EmmettJ.Butler Pero abc debería implementarse después de todo. ¿Tengo razón?

    – Nabín

    24 de mayo de 2017 a las 9:13

  • @Spiderman no, abc no es para clases base simples, abc es cuando necesita verificaciones de instancia, similar a isinstance (x, número) o isinstance (x, collections.abc.Sequence).

    -Stan Prokop

    8 de junio de 2017 a las 18:28

  • @ EmmettJ.Butler Pero no proporciona ningún comentario inmediato. Proporciona la retroalimentación solo después de que se utilizó el método, lo que hace que este mecanismo sea completamente inútil como clase abstracta.

    – Blazej Michalik

    9 de julio de 2018 a las 10:53

avatar de usuario
pifunc

Algo así, usando ABC

import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta
    
    @abc.abstractmethod
    def method_to_implement(self, input):
        """Method documentation"""
        return
    

Lea también este buen tutorial: https://pymotw.com/3/abc/

También puedes consultar zope.interfaz que se utilizó antes de la introducción de ABC en python.

  • ¿Agregar esto afecta las otras cosas también? es decir, ¿será necesario cambiar otra parte de mi código de clase?

    usuario506710

    8 de diciembre de 2010 a las 0:03

  • @user506710: La única otra parte de su código que podría necesitar cambiarse es si ya está usando una metaclase. En este caso, solo haga que su metaclase se derive de abc.ABCMeta en lugar de de type y deberías estar bien. Si no sabe qué es una metaclase, no se preocupe. 🙂

    – tipo todo

    8 de diciembre de 2010 a las 0:15

  • El tutorial vinculado es mucho más claro que el documento abc de Python (docs.python.org/2/library/abc.html); sin embargo dicen que la misma cosa.

    – Jacob Mármol

    26 de febrero de 2013 a las 5:02


  • Esta sintaxis no funciona en python 3, consulte esta pregunta/respuesta: stackoverflow.com/a/18513858/1240268.

    –Andy Hayden

    13 de septiembre de 2014 a las 2:37

  • En python 3, debería usar esta respuesta en su lugar. stackoverflow.com/a/13646263/5093308

    – Zhou Hongbo

    23 de junio de 2020 a las 7:09


avatar de usuario
tipo todo

Ver el módulo abc. Básicamente, usted define __metaclass__ = abc.ABCMeta en la clase, luego decora cada método abstracto con @abc.abstractmethod. Las clases derivadas de esta clase no se pueden instanciar a menos que se hayan invalidado todos los métodos abstractos.

Si su clase ya está usando una metaclase, derivarla de ABCMeta más bien que type y puede continuar usando su propia metaclase.

Una alternativa barata (y la mejor práctica antes de la abc se introdujo el módulo) sería hacer que todos sus métodos abstractos solo generen una excepción (NotImplementedError es bueno) por lo que las clases derivadas de él tendrían que anular ese método para ser útil.

sin embargo, el abc La solución es mejor porque evita que se creen instancias de tales clases (es decir, “falla más rápido”), y también porque puede proporcionar una implementación predeterminada o base de cada método al que se puede llegar usando el super() función en clases derivadas.

  • you can provide a default or base implementation of each method that can be reached using the super() function in derived classes no puedes usar super() sin que abc?

    – Andrea Bergonzo

    20/09/2017 a las 13:31

  • Lo que dice es que debes usar super() para alcanzar implementaciones de métodos en un ABC, no es que necesite usar abc para usar super(). super() funciona con cualquier clase de nuevo estilo.

    – tipo todo

    20 sep 2017 a las 18:41

  • Solo estaba confundido porque era “abc es mejor para la motivación 1 y la motivación 2”, pero la motivación 2 no lo es. abc específico 🙂

    – Andrea Bergonzo

    20/09/2017 a las 21:35

avatar de usuario
erizo demente

Las clases base abstractas son magia profunda. Periódicamente implemento algo usándolos y me sorprende mi propia inteligencia, muy poco después me encuentro completamente confundido por mi propia inteligencia (aunque esto bien puede ser solo una limitación personal).

Otra forma de hacer esto (debería estar en las bibliotecas estándar de Python si me preguntas) es hacer un decorador.

def abstractmethod(method):
    """
    An @abstractmethod member fn decorator.
    (put this in some library somewhere for reuse).

    """
    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method ' 
                                  + repr(method))
    default_abstract_method.__name__ = method.__name__    
    return default_abstract_method


class Shape(object):

    def __init__(self, shape_name):
       self.shape = shape_name

    @abstractmethod
    def foo(self):
        print "bar"
        return

class Rectangle(Shape):
    # note you don't need to do the constructor twice either
    pass  

r = Rectangle("x")
r.foo()

Yo no escribí el decorador. Se me acaba de ocurrir que alguien lo habría hecho. Lo puedes encontrar aquí: http://code.activestate.com/recipes/577666-abstract-method-decorator/ Buena una jimmy2times. Tenga en cuenta la discusión en la parte inferior de esa página sobre la seguridad del decorador. (Eso podría arreglarse con el módulo de inspección si alguien estuviera tan inclinado).

avatar de usuario
Adán norberg

No se puede, con lenguaje primitivo. Como se ha dicho, el paquete abc proporciona esta funcionalidad en Python 2.6 y versiones posteriores, pero no hay opciones para Python 2.5 y versiones anteriores. los abc el paquete no es una característica nueva de Python; en cambio, agrega funcionalidad al agregar explícito “¿esta clase dice que hace esto?” comprobaciones, con comprobaciones de coherencia implementadas manualmente para provocar un error durante la inicialización si dichas declaraciones se realizan de forma falsa.

Python es un lenguaje militantemente tipificado dinámicamente. No especifica primitivas de lenguaje que le permitan evitar que un programa se compile porque un objeto no cumple con los requisitos de tipo; esto solo se puede descubrir en tiempo de ejecución. Si necesita que una subclase implemente un método, documente eso y luego simplemente llame al método con la ciega esperanza de que estará allí.

Si está ahí, fantástico, simplemente funciona; se llama pato escribiendo, y su objeto ha graznado lo suficiente como un pato para satisfacer la interfaz. Esto funciona bien incluso si self es el objeto al que llamas un método de este tipoa los efectos de anulaciones obligatorias debido a métodos base que necesitan implementaciones específicas de características (funciones genéricas), porque self es una convención, no algo realmente especial.

La excepción está en __init__porque cuando se llama a su inicializador, el inicializador del tipo derivado no lo ha hecho, por lo que aún no ha tenido la oportunidad de engrapar sus propios métodos en el objeto.

Si el método no se implementó, obtendrá un AttributeError (si no está allí) o un TypeError (si hay algo con ese nombre pero no es una función o no tenía esa firma). Depende de usted cómo manejar eso: llámelo error del programador y deje que bloquee el programa (y “debería” ser obvio para un desarrollador de python qué causa ese tipo de error allí: una interfaz de pato no satisfecha), o atrapar y manejar esas excepciones cuando descubre que su objeto no era compatible con lo que deseaba que hiciera. De hecho, capturar AttributeError y TypeError es importante en muchas situaciones.

  • ¿Qué pasa con el módulo abc mencionado en la otra respuesta, entonces?

    usuario506710

    8 de diciembre de 2010 a las 0:06

  • Por lo que vale, Python es fuertemente tipado, es solo dinámicamente tipado.

    – Gintautas Miliauskas

    8 de diciembre de 2010 a las 0:09

  • Ack. Esta es la primera vez que oigo hablar de la abc módulo. Sin embargo, dejo esta respuesta porque se aplica a las versiones de Python anteriores a la 2.6 (CentOS se envía con la 2.4), y la tipificación pato todavía funciona aquí.

    – Adam Norberg

    8 de diciembre de 2010 a las 0:35

  • Sí, es un buen punto sobre el tipo de pato. En general, al programar en Python, generalmente será más feliz llamando a los métodos que espera en lugar de imponer restricciones de tipo real. los abc es una buena opción para especificar que tipo de pato que quieras, sin embargo.

    – tipo todo

    8 de diciembre de 2010 a las 1:11


  • python ciertamente está fuertemente tipado. se refiere a la tipificación estática frente a la dinámica.

    –Corey Goldberg

    8 de diciembre de 2010 a las 3:30

avatar de usuario
antonio giorgio

Puede usar six y abc para construir una clase para python2 y python3 de manera eficiente de la siguiente manera:

import six
import abc

@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
    """
    documentation
    """

    @abc.abstractmethod
    def initialize(self, para=None):
        """
        documentation
        """
        raise NotImplementedError

Este es un documento impresionante de la misma.

  • ¿Qué pasa con el módulo abc mencionado en la otra respuesta, entonces?

    usuario506710

    8 de diciembre de 2010 a las 0:06

  • Por lo que vale, Python es fuertemente tipado, es solo dinámicamente tipado.

    – Gintautas Miliauskas

    8 de diciembre de 2010 a las 0:09

  • Ack. Esta es la primera vez que oigo hablar de la abc módulo. Sin embargo, dejo esta respuesta porque se aplica a las versiones de Python anteriores a la 2.6 (CentOS se envía con la 2.4), y la tipificación pato todavía funciona aquí.

    – Adam Norberg

    8 de diciembre de 2010 a las 0:35

  • Sí, es un buen punto sobre el tipo de pato. En general, al programar en Python, generalmente será más feliz llamando a los métodos que espera en lugar de imponer restricciones de tipo real. los abc es una buena opción para especificar que tipo de pato que quieras, sin embargo.

    – tipo todo

    8 de diciembre de 2010 a las 1:11


  • python ciertamente está fuertemente tipado. se refiere a la tipificación estática frente a la dinámica.

    –Corey Goldberg

    8 de diciembre de 2010 a las 3:30

¿Ha sido útil esta solución?

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
Privacidad