monkey duck python design interface

python - monkey - Duck typing y(java) concepto de interfaz



duck typing ruby (3)

No quiero que mi aplicación falle (al azar) en el medio de su proceso solo porque John fingió ser un pato, pero supongo que no sería una buena idea verificar cada atributo del objeto cuando lo recibo ...?

Esa es una cuestión de tipeo dinámico en general. En un lenguaje estáticamente tipado como Java, el compilador comprueba en tiempo de compilación si la Person implementa IDuck o no. En un lenguaje de tipado dinámico como Python, se obtiene un error en tiempo de ejecución si la Person pierde alguna característica de pato en particular (como swim ). Para citar otro artículo de Wikipedia ( "Sistema de tipo", sección "Escritura dinámica" ):

El tipado dinámico puede dar como resultado errores del 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. Dichos 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 no debería tener. Esto puede hacer que el error sea difícil de localizar.

La tipificación dinámica tiene sus desventajas (usted ha mencionado una) y sus ventajas. Se puede encontrar una breve comparación en otra sección del artículo del sistema Tipo en Wikipedia: comprobación estática y dinámica de tipos en la práctica .

Acabo de leer el artículo de Wikipedia sobre el tipado de patos , y siento que omito 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()

¿Qué in_the_forest si, en in_the_forest , escribo:

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

y luego, porque sé que es un pato, ¿quiero swim ? john se hundirá!

No quiero que mi aplicación falle (al azar) en el medio de su proceso solo porque John fingió ser un pato, pero supongo que no sería una buena idea verificar cada atributo del objeto cuando lo recibo ...?


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 patos. Fue diseñado para operar con un Duck . Un Duck puede hacer el quack y las feathers , por lo que el codificador usó esas características para realizar el trabajo. En este caso, el hecho de que un Duck también puede swim no se usó, 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 una Person (que también podía hacer el quack y las feathers ) y quería reutilizar la función, no tuvieron suerte. ¿Es una Person una subclase de Duck ? No, eso no parece apropiado. ¿Hay una interfaz QuacksAndFeathers ? Tal vez, si tenemos suerte. De lo contrario, tendremos que hacer uno, ir a modificar Duck para implementarlo, y modificar in_the_forest para tomar un QuacksAndFeathers lugar de un Duck . Esto puede ser imposible si Duck está en una biblioteca externa.

En Python, simplemente pasas tu Persona a in_the_forest y funciona. Porque resulta que en el in_the_forest no necesita un Duck , solo necesita un objeto parecido a un pato, y en este caso la persona es lo suficientemente parecida a un pato.

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

Ahora bien, es cierto que Java habría detectado este error en el momento de la compilación y Python no. Eso puede verse como una desventaja. El argumento del contador pro-dinámico es decir que cualquier cuerpo sustancial de código que escriba contendrá siempre errores que ningún compilador puede detectar (y para ser honesto, Java ni siquiera es un ejemplo particularmente bueno de un compilador con fuertes controles estáticos para atrapar muchos errores). Entonces debes probar tu código para encontrar esos errores. Y si estás probando esos errores, encontrarás trivialmente errores donde pasaste a una Person a una función que necesita un Duck . Dado que, dice el mecanógrafo dinámico, un lenguaje que lo tienta a no probar porque encuentra algunos de sus errores triviales es realmente algo malo . Y además de eso, te impide hacer cosas realmente útiles, como reutilizar la función in_the_forest en una Person .

Personalmente estoy desgarrado en dos direcciones. Realmente me gusta Python con su tipado dinámico flexible. Y realmente me gusta Haskell y Mercury por sus poderosos sistemas de tipo estático. No soy muy fanático de Java o C ++; en mi opinión, tienen todos los bits malos de tipeo estático con algunos de los bits buenos.


No se puede hablar en otros idiomas, pero en Python, recientemente (v2.6) introdujo el módulo de clases abstractas de base (ABC) .

Si lee los motivos de su introducción ( PEP 3119 ) rápidamente se dará cuenta de que parte de la razón fue "salvar a John de una muerte segura" o, en otras palabras, para facilitar el control del hecho cuando programa en una interfaz, todo los métodos de interfaz estarán allí. Del PEP vinculado:

Los ABC son simplemente clases de Python que se agregan al árbol de herencia de un objeto 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, los ABC definen un conjunto mínimo de métodos que establecen el comportamiento característico del tipo. El código que discrimina objetos según su tipo ABC puede confiar en que esos 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 complemento funcione, y luego puede crear varios complementos diferentes subclassing. Dependiendo de si cada complemento debe o no tener esos métodos definidos, puede definir los métodos BasePlugin para pasar silenciosamente (los complementos pueden definir esos métodos) o para generar una excepción (los complementos deben definir esos métodos / anular el de BasePlugin ).

EDITAR: En el hilo de los comentarios a continuación, se me sugirió incluir en la respuesta esta pequeña discusión:

Este tipo de características, al menos en Python, no se implementan por el programador humano (Python nunca silencia un error, por lo que ya hay muchos comentarios), sino por la propia capacidad de introspección de Python (facilitando así la tarea). para escribir carga dinámica, código de metaprogramación, etc. ...). En otras palabras: sé que John no puede volar ... ¡pero quiero que el intérprete de Python también lo sepa! :)