¿Cuál es la versión de Python para “Código contra una interfaz, no un objeto”?

Inspirado por una gran pregunta (y un montón de grandes respuestas) desde aquí.

¿La statement “Código contra una interfaz, no un objeto” tiene algún significado en Python?

Estoy buscando respuestas como las de la pregunta original pero con fragmentos y pensamientos de Python.

“El código contra una interfaz, no un objeto” no tiene sentido literal en Python porque el idioma no tiene una función de interfaz. El equivalente aproximado de Python es “usar tipografía de pato”. Si quiere ver si un objeto es un pato, en otras palabras, debe verificar si tiene un método quack() , o mejor aún, intentar quack() y proporcionar un manejo de errores adecuado, no realizar pruebas para ver si Es una instancia de Duck .

Los tipos de pato comunes en Python son archivos (bueno, en realidad, objetos similares a archivos), mapeos (objetos similares a dict ), callables (objetos similares a funciones), secuencias (objetos parecidos a list ), e iterables (cosas que se pueden repetir). , que pueden ser contenedores o generadores).

Como ejemplo, las características de Python que desean un archivo generalmente estarán felices de aceptar un objeto que implemente los métodos de file que necesita; no tiene que derivarse de la clase de file . Para usar un objeto como estándar, por ejemplo, lo principal que va a necesitar es un método write() (y tal vez flush() y close() , que en realidad no necesita hacer nada). De manera similar, un objeto llamable es cualquier objeto que tiene un __call__() ; no es necesario derivarlo del tipo de función (de hecho, no se puede derivar del tipo de función).

Usted debe tomar un enfoque similar. Verifique los métodos y atributos que necesita para lo que va a hacer con un objeto. Mejor aún, documente lo que espera y asum que quienquiera que esté llamando a su código no es un completo tonto. (Si le dan un objeto que no puede usar, seguramente lo resolverán con la rapidez suficiente de los errores que cometen). Haga pruebas para tipos específicos solo cuando sea necesario. A veces es necesario, por lo que Python le proporciona type() , isinstance() y issubclass() , pero tenga cuidado con ellos.

La tipificación de pato de Python es equivalente a “código contra una interfaz, no un objeto” en el sentido de que se le recomienda no hacer que su código sea demasiado dependiente del tipo de un objeto, sino más bien para ver si tiene la interfaz que necesita. La diferencia es que en Python, “interfaz” solo significa un conjunto informal de atributos y métodos de un objeto que proporciona un determinado comportamiento, en lugar de un constructo de lenguaje llamado específicamente interface .

Puede formalizar las “interfaces” de Python hasta cierto punto utilizando el módulo abc , lo que le permite declarar que una clase dada es una subclase de una “clase base abstracta” (interfaz) dada utilizando cualquier criterio que desee, como “tiene atributos color , tail_length , y quack , y quack es llamable “. Pero esto sigue siendo mucho menos estricto que los lenguajes estáticos que tienen una función de interfaz.

Para entender las interfaces en Python tienes que entender la tipificación de pato. Desde el mismo glosario de Python:

duck-typing : un estilo de progtwigción que no se ve en el tipo de un objeto para determinar si tiene la interfaz correcta; en cambio, el método o atributo simplemente se llama o usa (“Si se parece a un pato y a los curanderos como a un pato, debe ser un pato”). Al enfatizar las interfaces en lugar de los tipos específicos, el código bien diseñado mejora su flexibilidad al permitir Sustitución polimórfica. La tipificación de pato evita las pruebas con type () o isinstance (). (Sin embargo, tenga en cuenta que la tipificación de pato puede complementarse con clases base abstractas). En su lugar, generalmente emplea las pruebas hasattr () o la progtwigción EAFP.

Python fomenta la encoding de interfaces, solo que no se aplican, sino por convención. Los conceptos como iterables, callables o la interfaz del archivo son muy comunes en Python, así como los elementos integrados que se basan en interfaces como mapa, filtro o reducción.

Una interfaz significa que espera que ciertos métodos estén presentes y estandarizados en todos los objetos; ese es el punto de una interfaz o una clase base abstracta, o cualquier implementación que desee considerar.

Por ejemplo (Java), uno podría tener una interfaz para el cifrado simétrico, como por ejemplo:

 public interface cipher { public void encrypt(byte[] block, byte[] key); public void decrypt(byte[] block, byte[] key); } 

Entonces puedes implementarlo:

 public class aes128 implements cipher { public void encrypt(byte[] block, byte[] key) { //... } public void decrypt(byte[] block, byte[] key) { //... } } 

Entonces es posible declarar un objeto así:

 cipher c; 

¿Qué hemos hecho aquí? Bueno, hemos creado este objeto c cuyo tipo debe coincidir con el de la interfaz. c puede referirse a cualquier cosa que coincida con esta interfaz, por lo que la siguiente etapa sería:

 c = new aes128(); 

Ahora puedes llamar a los métodos que esperas que tenga un cipher .

Eso es java. Ahora esto es lo que haces en python:

 class aes128(Object): def __init__(self): pass def encrypt(self, block, key): # here I am going to pass, but you really # should check what you were passed, it could be # anything. Don't forget, if you're a frog not a duck # not to quack! pass 

Cuando quiera usar esto y no esté seguro de que el objeto que se le ha pasado sea, simplemente intente usarlo:

 c = aes128() try: c.encrypt(someinput, someoutput) except: print "eh? No encryption method?!" 

Aquí, está confiando en la implementación de c.encrypt para raise si no puede manejar lo que se ha pasado, si existe el método. Por supuesto, si c es un tipo de cadena y, por lo tanto, no es del tipo correcto que necesita, también se lanzará automáticamente y lo capturará (con suerte).

En resumen, una forma de progtwigción se escribe de tal manera que debe obedecer las reglas de la interfaz, la otra dice que ni siquiera necesita escribirlas, simplemente confía en que si no fallaba, funcionaba.

Espero que te muestre la diferencia práctica entre los dos.

¿Cuál es la versión de Python para “Código contra una interfaz, no un objeto”?

La cita correcta es ” progtwig contra una interfaz, no una implementación “. El principio es el mismo en Python que en Smalltalk, el idioma en el que se originó.

Hace la statement “Código contra una interfaz, no un objeto”. ¿Tiene algún significado en Python?

Sí. Tiene el mismo significado en Python que en el idioma con el que se originó esta cita (Smalltalk), y en todos los demás idiomas.