Porque estoy acostumbrado a las viejas formas de pato a escribir en Python, no entiendo la necesidad de ABC (clases base abstractas). El ayuda es buena sobre cómo usarlos.

Traté de leer la explicación en el PEP, pero se fue por encima de mi cabeza. Si estaba buscando un contenedor de secuencias mutables, me gustaría comprobar por __setitem__, o más probablemente la use (EAFP). No he venido a través de un uso de la vida real para la los números módulo, que hace uso de Abc, pero que es el más cercano que tengo a la comprensión.

¿Alguien puede explicar la lógica para mí, por favor?

5 Comentarios

  1. 144

    Versión corta

    Abc ofrecen un mayor nivel de semántica contrato entre los clientes y la implementación de las clases.

    Versión larga

    Hay un contrato entre una clase y sus llamadores. La clase de promesas a hacer ciertas cosas y tener ciertas propiedades.

    Hay diferentes niveles en el contrato.

    A un nivel muy bajo, el contrato podría incluir el nombre de un método o de su número de parámetros.

    En un staticly-lenguaje escrito, contrato que en realidad iba a ser ejecutada por el compilador. En Python, puede utilizar EAFP o introspección para confirmar que el objeto desconocido cumple este esperado contrato.

    Pero también hay de más alto nivel, semántica promesas en el contrato.

    Por ejemplo, si hay un __str__() método, se espera para devolver una representación de cadena del objeto. Es podría eliminar todos los contenidos de el objeto de confirmar la transacción y escupir una página en blanco de la impresora… pero hay un entendimiento común de lo que se debe hacer, que se describe en el manual de Python.

    Que es un caso especial, donde la semántica contrato se describe en el manual. ¿Qué debe hacer el print() método de hacer? Debe escribir el objeto a una impresora o a una línea de la pantalla, o algo más? Depende – tienes que leer los comentarios para entender el contrato completo aquí. Un trozo de código de cliente que simplemente comprueba que el print() existe un método ha confirmado parte del contrato – que una llamada al método se puede hacer, pero no hay acuerdo en el más alto nivel de la semántica de la llamada.

    La definición de una Clase Base Abstracta (ABC) es un modo de producción de un contrato entre la clase de las entidades ejecutoras y las personas que llaman. No es sólo una lista de nombres de método, sino de una comprensión compartida de lo que esos métodos deben hacer. Si usted hereda de este ABC, que son prometedores para seguir todas las reglas descritas en el apartado de comentarios, incluyendo la semántica de la print() método.

    Python duck-typing tiene muchas ventajas en flexibilidad sobre la estática de escribir, pero no resuelve todos los problemas. Abc de ofrecer una solución intermedia entre la forma libre de Python y de la servidumbre y la disciplina de un staticly-lenguaje escrito.

    • Creo que tienes un punto, pero no puedo seguirte. Entonces, ¿cuál es la diferencia, en términos de contrato, entre una clase que implementa __contains__ y una clase que hereda de collections.Container? En tu ejemplo, en Python, siempre hubo un entendimiento compartido de __str__. La aplicación de __str__ hace las mismas promesas como la herencia de algunos de ABC y, a continuación, la aplicación de __str__. En ambos casos se puede romper el contrato; no hay comprobable semántica, tales como los que tenemos en estático.
    • collections.Container es un caso de degeneración, que sólo incluye \_\_contains\_\_, y sólo a media predefinidas de la convención. El uso de un ABC no agregar mucho valor por sí mismo, estoy de acuerdo. Yo sospecho que fue agregado para permitir (por ej.) Set heredar de ella. En el momento de llegar a Set, de repente pertenecientes a la ABC ha considerable semántica. Un elemento puede pertenecer a la colección dos veces. Que NO es detectable por la existencia de métodos.
    • El Set ejemplo se ve mejor para mí que el print() función. En particular, yo no entendía el nivel más alto de contrato de la print() método. Me parece similar a la __contains__ y __str__ casos, donde la semántica de estos están documentados en sus respectivos ayuda/comentarios. Pero cuando tienes un objeto complejo como Set, la semántica no la semántica de cualquier función o propiedad del objeto. Me estoy perdiendo algo?
    • Sí, creo que Set es un mejor ejemplo de lo que print(). Yo estaba tratando de encontrar un método de nombre cuyo significado era ambigua, y no podía ser grokked por el nombre solo, así que no podría estar seguro de que haría lo correcto sólo por su nombre y el de Python manual.
    • Así que es como una especie de interfaz en dicha servidumbre de los lenguajes de programación?
    • Cualquier oportunidad de volver a escribir la respuesta con Set como el ejemplo en lugar de print? Set hace un montón de sentido, @Oddthinking.
    • Creo que este artículo lo explica muy bien: dbader.org/blog/abstract-base-classes-in-python
    • Esto es realmente una gran respuesta! gracias!
    • No entiendo cómo es ABC hacer cumplir la semántica del contrato. No es la clase derivada es libre para poner en práctica lo que quiera y romper cualquier documentado semántica contrato?
    • No es forzada. Es un.compromiso por un programador, que otro programador puede buscar.

  2. 208

    @Oddthinking la respuesta no está mal, pero creo que se pierde el real, práctica razón Python tiene el Abecedario en un mundo de pato a escribir.

    Los métodos abstractos son bonitas, pero en mi opinión, realmente no rellene los casos de uso que no estén ya cubiertas por duck typing. Clases base abstractas’ poder real se encuentra en la forma en que permiten personalizar el comportamiento de isinstance y issubclass. (__subclasshook__ es, básicamente, una más amigable de la API en la parte superior de Python __instancecheck__ y __subclasscheck__ ganchos.) La adaptación de construido-en construcciones de trabajo personalizados de tipos es una parte muy importante de Python de la filosofía.

    De Python de código fuente es ejemplar. Aquí es cómo collections.Container se define en el estándar de la biblioteca (en el momento de la escritura):

    class Container(metaclass=ABCMeta):
        __slots__ = ()
    
        @abstractmethod
        def __contains__(self, x):
            return False
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Container:
                if any("__contains__" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented

    Esta definición de __subclasshook__ dice que cualquier clase con un __contains__ atributo se considera para ser una subclase de Contenedor, incluso si no subclase directamente. Para que yo pueda escribir esto:

    class ContainAllTheThings(object):
        def __contains__(self, item):
            return True
    
    >>> issubclass(ContainAllTheThings, collections.Container)
    True
    >>> isinstance(ContainAllTheThings(), collections.Container)
    True

    En otras palabras, si usted aplicar el derecho de la interfaz, eres una subclase! Abc proporcionar una manera formal para definir las interfaces en Python, aunque se mantiene fiel al espíritu de pato a escribir. Además, esto funciona de una manera que honra a los Abierto-Cerrado De Principio.

    Python modelo de objetos se ve superficialmente similar a la de una forma más «tradicional» O sistema (por lo que me refiero Java*) – llegamos a tus clases, tus objetos, yer métodos – pero cuando usted se rasca la superficie sobre la que vas a encontrar algo mucho más rico y más flexible. Asimismo, Python de la noción de clases base abstractas pueden ser reconocible para un desarrollador de Java, pero en la práctica está destinada a un fin muy distinto.

    A veces me encuentro escribiendo funciones polimórficas que pueden actuar sobre un solo elemento o de una colección de elementos, y me encuentro con isinstance(x, collections.Iterable) a ser mucho más legible que hasattr(x, '__iter__') o un equivalente try...except bloque. (Si usted no sabe de Python, que de los tres tendría la intención de que el código más claro?)

    Que dijo, me parece que rara vez se necesita para escribir mi propio ABC y me suelen descubrir la necesidad de uno a través de refactorización. Si veo a una función polimórfica haciendo un montón de atributo de cheques, o un montón de funciones que el mismo atributo de cheques, ese olor sugiere la existencia de un ABC de espera para ser extraído.

    *sin entrar en el debate sobre si Java es «tradicional» O sistema de…


    Anexo: a pesar de que una clase base abstracta puede reemplazar el comportamiento de isinstance y issubclass, todavía no entra en el MRO de la virtual subclase. Este es un riesgo potencial para los clientes: no todos los objetos para los que isinstance(x, MyABC) == True tiene los métodos definidos en MyABC.

    class MyABC(metaclass=abc.ABCMeta):
        def abc_method(self):
            pass
        @classmethod
        def __subclasshook__(cls, C):
            return True
    
    class C(object):
        pass
    
    # typical client code
    c = C()
    if isinstance(c, MyABC):  # will be true
        c.abc_method()  # raises AttributeError

    Lamentablemente este es uno de esos «no lo hagas» trampas (de la que Python tiene relativamente pocos!): evitar definir el Abc con una __subclasshook__ y no los métodos abstractos. Además, usted debe hacer su definición de __subclasshook__ coherente con el conjunto de métodos abstractos de su ABC define.

    • «si usted aplicar el derecho de la interfaz, usted es una subclase de» muchas Gracias por eso. No sé si Oddthinking alto, pero yo lo hice. FWIW, isinstance(x, collections.Iterable) es más claro para mí, y yo sé de Python.
    • Excelente post. Gracias. Creo que en la Adición, «no lo hagas» trampa», que es un poco como hacer normal subclase herencia, pero luego tener la C subclase eliminar (o tornillo hasta más allá de la reparación) la abc_method() heredado de MyABC. La principal diferencia es que es la superclase que es cagarla de la herencia del contrato, no de la subclase.
    • ¿Usted no tiene que hacer Container.register(ContainAllTheThings) para el ejemplo dado para el trabajo?
    • El código de la respuesta obras! ¡Pruébalo! El significado de __subclasshook__ es «cualquier clase que satisface este predicado se considera una subclase para los fines de isinstance y issubclass cheques, independientemente de si se ha registrado con el ABC y independientemente de si es una subclase directa«. Como dije en la respuesta, si usted aplicar el derecho de la interfaz, eres una subclase!
    • Sí, claro que lo hace, yo sólo estaba siendo ciego a un error en mi código, gracias :-). Gran post. Debo conseguir algunos IDE.
    • Usted debe cambiar el formato de cursiva y en negrita. «si usted aplicar el derecho de la interfaz, eres una subclase!» Muy concisa explicación. Gracias!
    • Excelente post!!!
    • Gran explicación. En este sentido es el ABC de la clase similar a Golang del concepto de interfaz: «en el caso de implementar los métodos, eres un ejemplo»?

  3. 99

    Una útil característica de Abc es que si no implementar todos los métodos necesarios (y propiedades), se obtiene un error en la creación de instancias, en lugar de un AttributeError, potencialmente, mucho más tarde, cuando en realidad se trata de utilizar el método que falta.

    from abc import ABCMeta, abstractmethod
    
    # python2
    class Base(object):
        __metaclass__ = ABCMeta
    
        @abstractmethod
        def foo(self):
            pass
    
        @abstractmethod
        def bar(self):
            pass
    
    # python3
    class Base(object, metaclass=ABCMeta):
        @abstractmethod
        def foo(self):
            pass
    
        @abstractmethod
        def bar(self):
            pass
    
    class Concrete(Base):
        def foo(self):
            pass
    
        # We forget to declare `bar`
    
    
    c = Concrete()
    # TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

    Ejemplo de https://dbader.org/blog/abstract-base-classes-in-python

    Edit: para incluir python3 sintaxis, gracias @PandasRocks

    • El ejemplo y el vínculo de ambos fueron muy útiles. Gracias!
    • mejor, más claro ejemplo. Instantánea de la comprensión de la
    • si la Base se define en un archivo separado, debe heredar de él como Base.La Base o el cambio de la línea de importación a ‘de la Base de la importación de la Base’
    • Cabe señalar que en Python3 la sintaxis es ligeramente diferente. Ver esta respuesta: stackoverflow.com/questions/28688784/…
    • Viniendo de un C#, esta es la el la razón para el uso de clases abstractas. Usted está proporcionando la funcionalidad, sino que indica que la funcionalidad que requiere la implementación de más. Las otras respuestas parecen faltar en este punto.
  4. 16

    Se hará la determinación de si un objeto soporta un protocolo dado, sin tener que comprobar la presencia de todos los métodos en el protocolo o sin activación de una excepción de profundidad en el «enemigo» territorio debido a la falta de apoyo mucho más fácil.

  5. 5

    Método abstracto asegúrese de que el método que se llama en la clase de padre que tiene que aparecer en el niño de la clase. A continuación se noraml forma de las llamadas y el uso de abstractos.
    El programa escrito en python3

    Normal modo de llamada de

    class Parent:
    def methodone(self):
        raise NotImplemented()
    
    def methodtwo(self):
        raise NotImplementedError()
    
    class Son(Parent):
       def methodone(self):
           return 'methodone() is called'
    
    c = Son()
    c.methodone()

    ‘methodone() es llamado’

    c.methodtwo()

    NotImplementedError

    Con método Abstracto

    from abc import ABCMeta, abstractmethod
    
    class Parent(metaclass=ABCMeta):
        @abstractmethod
        def methodone(self):
            raise NotImplementedError()
        @abstractmethod
        def methodtwo(self):
            raise NotImplementedError()
    
    class Son(Parent):
        def methodone(self):
            return 'methodone() is called'
    
    c = Son()

    TypeError: no se Puede crear una instancia de clase abstracta Hijo con métodos abstractos methodtwo.

    Desde methodtwo no se llama niño en clase hemos obtenido un error. La correcta aplicación está por debajo de

    from abc import ABCMeta, abstractmethod
    
    class Parent(metaclass=ABCMeta):
        @abstractmethod
        def methodone(self):
            raise NotImplementedError()
        @abstractmethod
        def methodtwo(self):
            raise NotImplementedError()
    
    class Son(Parent):
        def methodone(self):
            return 'methodone() is called'
        def methodtwo(self):
            return 'methodtwo() is called'
    
    c = Son()
    c.methodone()

    ‘methodone() es llamado’

    • Gracias. No es que el mismo punto en cerberos respuesta anterior?

Dejar respuesta

Please enter your comment!
Please enter your name here