Pregunta

Debido a que estoy acostumbrado a las viejas formas de tipificación de pato en Python, no llego a entender la necesidad de ABC (clases base abstractas). El ayuda es bueno en la forma de utilizarlos.

Me trató de leer la justificación en el PEP , pero fue sobre mi cabeza. Si yo estaba buscando un contenedor de secuencias mutables, me gustaría comprobar por __setitem__, o más probablemente tratar de usarlo ( EAFP ). No he encontrado con un uso de la vida real para los números módulo, que hace uso de ABC, pero eso es lo más cercano que tengo a la comprensión.

Puede alguien explicar la razón fundamental para mí, por favor?

¿Fue útil?

Solución

Versión corta

ABC ofrecen un mayor nivel de contrato semántica entre los clientes y las clases implementadas.

Versión larga

Hay un contrato entre una clase y sus interlocutores. Las promesas de clase para hacer ciertas cosas y tienen 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 su número de parámetros.

En un lenguaje escrito-staticly, que el contrato sería en realidad impuesta por el compilador. En Python, puede utilizar EAFP o la introspección para confirmar que el objeto desconocido cumple con este contrato se esperaba.

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

Por ejemplo, si hay un método __str__(), se espera para devolver una representación de cadena del objeto. Es podría borrar todo el contenido del objeto, confirmar la transacción y escupió una página en blanco de la impresora ... pero hay un entendimiento común de lo que debería hacer, descrito en el manual de Python.

Esto es un caso especial, cuando el contrato semántica se describe en el manual. ¿Qué debe hacer el método print()? En caso de que escribir el objeto a una impresora o una línea a la pantalla, o alguna otra cosa? Depende - que necesita para leer los comentarios de entender el contrato completo aquí. Un pedazo de código de cliente que simplemente comprueba que el método print() existe tiene confirmados parte del contrato -. Que una llamada al método se puede hacer, pero no que no hay acuerdo sobre la semántica de alto nivel de la llamada

Definición de una clase base abstracta (ABC) es una manera de producir un contrato entre los ejecutores de clase y las personas que llaman. No es sólo una lista de nombres de método, sino un entendimiento compartido de lo que esos métodos deben hacer. Si hereda de este ABC, nos hemos comprometido a seguir todas las reglas descritas en los comentarios, incluyendo la semántica del método print().

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

Otros consejos

respuesta

@ de Oddthinking no está mal, pero creo que no alcanza la real , práctica razón Python tiene ABC en un mundo de pato a escribir.

Los métodos abstractos son muy ordenada, pero en mi opinión que en realidad no se llenan los casos de uso que no estén cubiertas por la tipificación de pato. yace el poder real clases base abstractas en la forma en que le permiten personalizar el comportamiento de isinstance y issubclass . (__subclasshook__ es básicamente una API más amigable en la parte superior de Python __instancecheck__ y __subclasscheck__ ganchos.) la adaptación de una función de construcciones para trabajar en tipos personalizados es una parte muy importante de la filosofía de Python.

código fuente de Python es ejemplar. Aquí es como collections.Container se define en la biblioteca estándar ( 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 atributo __contains__ se considera que es una subclase de los recipientes, incluso si no subclase directamente. Así que puedo escribir lo siguiente:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

En otras palabras, si implementa la interfaz correcta, usted es una subclase! ABC proporcionan una manera formal para definir interfaces en Python, mientras se mantiene fiel al espíritu de pato a escribir. Además, esto funciona de una manera que hace honor al abierto-cerrado Principio .

de Python miradas modelo de objetos superficialmente similar a la de un sistema orientado a objetos más "tradicional" (y me refiero a Java *) - llegamos yer clases, yer objetos, yer métodos - pero cuando usted se rasca la superficie que encontrará algo mucho más rico y más flexible. Del mismo modo, la noción de clases base abstractas de Python puede ser reconocible para un desarrollador de Java, pero en la práctica están destinados a un propósito muy diferente.

A veces me encuentro escribiendo funciones polimórficas que pueden actuar sobre un artículo o una colección de artículos, y me parece isinstance(x, collections.Iterable) a ser mucho más fácil de leer que hasattr(x, '__iter__') o un bloque de try...except equivalente. (Si no sabe Python, ¿cuál de los tres haría que la intención de las más claras código?)

Dicho esto, me parece que rara vez necesito escribir mi propia ABC y que normalmente descubrir la necesidad de uno a través de refactorización. Si veo una función polimórfica hacer una gran cantidad de comprobaciones de atributos, o gran cantidad de funciones que hacen las mismas comprobaciones de atributos, que olor sugiere la existencia de un ABC espera de ser extraído.

* sin entrar en el debate sobre si Java es un sistema orientado a objetos "tradicional" ...


Adición : 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 subclase virtual. Este es un problema potencial para los clientes:. No todos los objetos para los cuales tiene isinstance(x, MyABC) == True 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

Desafortunadamente esto uno de los que "simplemente no hacen eso" trampas (de los cuales Python tiene relativamente pocos!): Evitar la definición de ABC con un tanto __subclasshook__ y métodos no abstractos. Por otra parte, usted debe hacer su definición de __subclasshook__ coherente con el conjunto de métodos abstractos sus define ABC.

Una característica muy útil de ABC es que si no se implementa todos los métodos necesarios (y propiedades) se produce un error durante la instanciación, en lugar de un AttributeError , potencialmente mucho más tarde, cuando en realidad se trata de usar 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- clases-en-pitón

Editar: para incluir la sintaxis python3, @PandasRocks gracias

Esto hará que la determinación de si un objeto compatible con un protocolo dado sin tener que verificar la presencia de todos los métodos en el protocolo o sin desencadenar una excepción en medio del territorio "enemigo" debido a la falta de apoyo mucho más fácil.

Extracto método de asegurarse de que lo sea el método que está llamando en la clase padre tiene que ser aparece en la clase de niño. A continuación se forma noraml de llamar y usar abstracta. El programa escrito en python3

Forma normal de llamar

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()
  
    

'metadona () se llama'

  
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:. ¿Puede la clase abstracta no instantiate Hijo con métodos abstractos methodtwo

  

Desde methodtwo no se pone en clase hija que ha obtenido un error. La aplicación adecuada es inferior a

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()
  
    

'metadona () se llama'

  
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top