Duck typing y (java) concepto de interfaz


Acabo de leer el artículo de Wikipedia sobre duck typing , y siento que me pierdo un punto importante sobre el concepto de interfaz que solía usar en Java:

"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."


class Duck:
    def quack(self):
        print("Quaaaaaack!")
    def feathers(self):
        print("The duck has white and gray feathers.")
    def swim(self):
        print("Swim seamlessly in the water")

class Person:
    def quack(self):
        print("The person imitates a duck.")
    def feathers(self):
        print("The person takes a feather from the ground and shows it.")
    def name(self):
        print("John Smith")

def in_the_forest(duck):
    duck.quack()
    duck.feathers()

def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)

game()

Y si, en in_the_forest, escribo:

  • ¿es quack como un pato ? sí
  • tiene un pato feathers ? sí
  • ¡genial, es un pato lo que tenemos !

Y después, porque sé que es un pato, quiero que swim? john se hundirá!

No quiero que mi aplicación se bloquee (aleatoriamente) en medio de su proceso solo porque John fingió ser un pato, pero supongo que no sería una buena idea comprobar cada uno de los atributos del objeto cuando lo recibo ... ?

Author: Kevin, 2011-07-05

3 answers

Duck typing no se trata realmente de verificar si las cosas que necesita están allí y luego usarlas. Duck typing se trata solo de usar lo que necesita.

La función in_the_forest fue escrita por un desarrollador que estaba pensando en ducks. Fue diseñado para operar en un Duck. A Duck can quack y feathers, por lo que el codificador utilizó esas características para realizar el trabajo en cuestión. En este caso, el hecho de que un Duck también puede swim no se utilizó, y no era necesario.

En un lenguaje estático como Java, in_the_forest habría sido declarado para tomar un Duck. Cuando el codificador descubrió más tarde que tenían un Person (que también podría quack y feathers) y quería reutilizar la función, se les acabó la suerte. Es un Person una subclase de Duck? No, eso no parece para nada apropiado. ¿Existe una interfaz QuacksAndFeathers? Tal vez, si tenemos suerte. De lo contrario tendremos que hacer uno, ir modificar Duck para implementarlo, y modificar in_the_forest para tomar un QuacksAndFeathers en lugar de un Duck. Esto puede ser imposible si Duck está en un biblioteca.

En Python, simplemente pasa tu Persona a in_the_forest y funciona. Porque resulta que in_the_forest no necesita un Duck, solo necesita un objeto "tipo pato", y en este caso la persona es suficientemente tipo pato.

game sin embargo, necesita una definición diferente de "pato-como", que es un poco más fuerte. Aquí, John Smith no tiene suerte.

Ahora, es cierto que Java habría captado este error en tiempo de compilación y Python no lo hará. Eso puede ser visto como una desventaja. El el argumento pro-dynamic-typing counter es decir que cualquier cuerpo sustancial de código que escriba siempre contendrá errores que ningún compilador puede atrapar (y para ser honesto, Java ni siquiera es un ejemplo particularmente bueno de un compilador con fuertes comprobaciones estáticas para atrapar muchos errores). Así que necesitas probar tu código para encontrar esos errores. Y si está probando esos errores, encontrará trivialmente errores donde pasó un Person a una función que necesita un Duck. Dado que, el mecanógrafo dinámico dice, un el lenguaje que te tienta a no a probar porque encuentra algunos de tus errores triviales es en realidad una cosa mala. Y encima de eso, te impide hacer algunas cosas realmente útiles, como reutilizar la función in_the_forest en un Person.

Personalmente estoy dividido en dos direcciones. Me gusta mucho Python con su escritura dinámica flexible. Y me gustan mucho Haskell y Mercury por sus potentes sistemas de tipo estático. No soy muy fan de Java o C++; en mi opinión, tienen todos los bits malos de escritura estática con algunos de los bits buenos.

 20
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-05 13:08:46

No puede hablar para otros lenguajes, pero en python se ha introducido recientemente (v2.6) el módulo Abstract Base Classes (ABC) .

Si lees la razón detrás de su introducción ( PEP 3119) te darás cuenta rápidamente de que parte de la razón era para "salvar a John de la muerte segura" o en otras palabras, para facilitar la verificación del hecho de que cuando programes en una interfaz, todos los métodos de la interfaz estarán allí. Del PEP vinculado:

Los ABCs son simplemente Python clases que se añaden a la herencia de un objeto árbol para señalar ciertas características de ese objeto a un inspector externo. Las pruebas se realizan usando isinstance (), y la presencia de un ABC particular significa que la prueba ha pasado. Además, el ABC define un conjunto mínimo de métodos que establecen la comportamiento característico del tipo. Código que discrimina objetos basados en en su tipo ABC puede confiar en que los los métodos siempre estarán presentes.

En general, puede aplicar el mismo patrón para su propio código. Por ejemplo: puede crear una clase BasePlugin con todos los métodos necesarios para que un plugin funcione, y luego puede crear varios plugins diferentes subclasificándolo. Dependiendo de si cada plugin debe o puede tener esos métodos definidos, puede definir BasePlugin métodos para pasar silenciosamente (plugins puede definir esos métodos) o para plantear una excepción (plugins debe definir esos métodos / anule el BasePlugin's).

EDITAR: En el hilo de comentarios a continuación, se me ha sugerido incluir en la respuesta este poco de discusión:

Este tipo de características - al menos en python-no se implementan por el bien del programador humano (python nunca silencia un error, por lo que ya hay mucha retroalimentación allí), sino por el bien de la propia capacidad de introspección de python (lo que facilita la escritura de carga dinámica, metaprogramación de código, sucesivamente...). En otras palabras: sé que John no puede volar... ¡pero quiero que el intérprete de Python lo sepa también! :)

 5
Author: mac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-05 14:11:55

No quiero que mi aplicación se bloquee (al azar) en medio de su proceso solo porque John fingió ser un pato, pero supongo que no sería una buena idea comprobar todos los atributos del objeto cuando lo reciba ... ?

Ese es un problema de la tipificación dinámica en general. En un lenguaje de tipo estático como Java, el compilador comprueba en tiempo de compilación si Person implementa IDuck o no. En un lenguaje dinámicamente escrito como Python, se obtiene un tiempo de ejecución error si Person pierde alguna característica particular de pato (como swim). Para citar otro artículo de Wikipedia ( "Type system", Sección "Dynamic Typing"):

La escritura dinámica puede dar lugar a errores de tipo de tiempo de ejecución, es decir, en tiempo de ejecución, un valor puede tener un tipo inesperado, y se aplica una operación sin sentido para ese tipo. Tales errores pueden ocurrir mucho después del lugar donde se cometió el error de programación,es decir, el lugar donde el tipo incorrecto de datos pasó a un lugar que debería no tengo. Esto puede hacer que el error sea difícil de localizar.

La tipificación dinámica tiene sus inconvenientes (usted ha mencionado uno) y sus ventajas. Una breve comparación se puede encontrar en otra sección del artículo del sistema de tipos en Wikipedia: Comprobación de tipos estática y dinámica en la práctica.

 4
Author: Heinzi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-05 08:49:36