En Java IoC / DI es una práctica muy común que se usa ampliamente en aplicaciones web, casi todos los marcos disponibles y Java EE. Por otro lado, también hay muchas aplicaciones web grandes de Python, pero además de Zope (que he oído que debería ser realmente horrible de codificar), IoC no parece ser muy común en el mundo de Python. (Por favor, nombra algunos ejemplos si crees que estoy equivocado).
Por supuesto, hay varios clones de frameworks Java IoC populares disponibles para Python, por ejemplo , springpython . Pero ninguno de ellos parece acostumbrarse prácticamente. Al menos, nunca me he topado con una aplicación web basada en Django o sqlalchemy + <insert your favorite wsgi toolkit here>
que usa algo así.
En mi opinión, IoC tiene ventajas razonables y facilitaría reemplazar el modelo django-default-user-model, por ejemplo, pero el uso extensivo de clases de interfaz e IoC en Python parece un poco extraño y no "pitónico". Pero tal vez alguien tenga una mejor explicación, por qué IoC no se usa ampliamente en Python.
Respuestas:
En realidad, no creo que DI / IoC sean tan infrecuentes en Python. Sin embargo, lo que es poco común son los marcos / contenedores DI / IoC .
Piénselo: ¿qué hace un contenedor DI? Te permite
Tenemos nombres para "cablear juntos" y "en tiempo de ejecución":
Por lo tanto, un contenedor DI no es más que un intérprete para un lenguaje de script dinámico. En realidad, permítanme reformular eso: un contenedor DI Java / .NET típico no es más que un intérprete deficiente para un lenguaje de script dinámico realmente malo con una sintaxis fea, a veces basada en XML.
Cuando programa en Python, ¿por qué querría usar un lenguaje de script feo y malo cuando tiene un lenguaje de script hermoso y brillante a su disposición? En realidad, esa es una pregunta más general: cuando programa en casi cualquier idioma, ¿por qué querría usar un lenguaje de script feo y malo cuando tiene Jython y IronPython a su disposición?
Entonces, para recapitular: la práctica de DI / IoC es tan importante en Python como en Java, exactamente por las mismas razones. La aplicación embargo de DI / COI, está integrado en el lenguaje y, a menudo tan ligero que desaparece por completo.
(Aquí hay un breve aparte para una analogía: en conjunto, una llamada de subrutina es un asunto bastante importante: debe guardar sus variables y registros locales en la memoria, guardar su dirección de retorno en algún lugar, cambiar el puntero de instrucción a la subrutina que está llamando, haga arreglos para que salte de nuevo a su subrutina cuando haya terminado, coloque los argumentos en algún lugar donde el destinatario pueda encontrarlos, y así sucesivamente. IOW: en el ensamblado, "llamada de subrutina" es un Patrón de diseño, y antes había idiomas como Para Fortran, que tenía llamadas de subrutina incorporadas, la gente estaba construyendo sus propios "marcos de subrutina". ¿Diría que las llamadas de subrutina son "poco comunes" en Python, simplemente porque no usa marcos de subrutina?)
Por cierto: para un ejemplo de lo que parece tomar DI a su conclusión lógica, echar un vistazo a Gilad Bracha 's neolengua Lenguaje de programación y sus escritos sobre el tema:
fuente
Parte de esto es la forma en que funciona el sistema de módulos en Python. Puede obtener una especie de "singleton" de forma gratuita, simplemente importándolo desde un módulo. Defina una instancia real de un objeto en un módulo, y luego cualquier código de cliente puede importarlo y obtener un objeto funcional, totalmente construido / poblado.
Esto está en contraste con Java, donde no importa instancias reales de objetos. Esto significa que siempre tiene que instanciarlos usted mismo (o usar algún tipo de enfoque de estilo IoC / DI). Puede mitigar la molestia de tener que crear una instancia usted mismo si tiene métodos de fábrica estáticos (o clases de fábrica reales), pero aún así incurrirá en la sobrecarga de recursos de crear nuevos cada vez.
fuente
MyClassInstances
clase para cada unoMyClass
en Java, que contiene solo instancias estáticas completamente inicializadas. Eso estaría conectado: Dfrom framework.auth.user import User
esto, podría ser mejor escribirUser = lookup('UserImplentation', 'framework.auth.user.User')
(el segundo parámetro podría ser un valor predeterminado) dentro del marco. Luego, los usuarios del marco podrían reemplazar / especializar laUser
implementación sin tocar el marco.IoC y DI son super comunes en código Python maduro. Simplemente no necesita un marco para implementar DI gracias a la escritura de pato.
El mejor ejemplo es cómo configurar una aplicación Django usando
settings.py
:Django Rest Framework utiliza DI en gran medida:
Déjame recordar ( fuente ):
fuente
IsAuthenticated
,ScopedRateThrottle
) son instanciadas por la clase. No se pasan al constructor.IsAuthenticated
yScopedRateThrottle
no son instancias, son clases. Se crean instancias cuando se crea un FooView (en realidad, cuando FooView procesa una solicitud). De todos modos, esto es simplemente un detalle de implementación.IsAuthenticated
yScopedRateThrottle
son las dependencias; se inyectan en elFooView
. No importa cuándo o cómo se haga esto. Python no es Java, por lo que hay diferentes formas de implementar esto.renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer)
. Burlarse es tan simple como@unittest.patch('myapp.views.FooView.permission_classes')
. La necesidad desesperada de "pasar algo" es una consecuencia de la "forma Java de hacer las cosas" debido a que Java es un lenguaje compilado y estáticamente tipado que carece de capacidades de metaprogramación fuertes.Django hace un gran uso de la inversión de control. Por ejemplo, el servidor de base de datos es seleccionado por el archivo de configuración, luego el marco proporciona instancias apropiadas de envoltura de base de datos a los clientes de la base de datos.
La diferencia es que Python tiene tipos de primera clase. Los tipos de datos, incluidas las clases, son en sí mismos objetos. Si quieres que algo use una clase en particular, simplemente nombra la clase. Por ejemplo:
El código posterior puede crear una interfaz de base de datos escribiendo:
En lugar de las funciones de fábrica repetitivas que Java y C ++ necesitan, Python lo hace con una o dos líneas de código ordinario. Esta es la fuerza de la programación funcional versus la imperativa.
fuente
import psycopg2 as database_interface
. Pon esa línea en ainjections.py
et voilà.self.database_interface
), que grita imperativo.Parece que las personas realmente ya no entienden lo que significa la inyección de dependencia y la inversión del control.
La práctica de utilizar la inversión de control es tener clases o funciones que dependan de otras clases o funciones, pero en lugar de crear las instancias dentro del código de la clase de función, es mejor recibirlo como parámetro, de modo que se pueda archivar un acoplamiento suelto. Eso tiene muchos beneficios como una mayor capacidad de prueba y para archivar el principio de sustitución de liskov.
Verá, al trabajar con interfaces e inyecciones, su código se vuelve más fácil de mantener, ya que puede cambiar el comportamiento fácilmente, ya que no tendrá que volver a escribir una sola línea de código (tal vez una o dos líneas en la configuración DI) de su clase para cambiar su comportamiento, ya que las clases que implementan la interfaz que su clase está esperando pueden variar independientemente siempre que sigan la interfaz. Una de las mejores estrategias para mantener el código desacoplado y fácil de mantener es seguir al menos los principios de responsabilidad única, sustitución e inversión de dependencia.
¿Para qué sirve una biblioteca DI si puede crear una instancia de un objeto usted mismo dentro de un paquete e importarlo para inyectarlo usted mismo? La respuesta elegida es correcta, ya que Java no tiene secciones de procedimiento (código fuera de las clases), todo lo que entra en la configuración aburrida xml, por lo tanto, la necesidad de una clase para instanciar e inyectar dependencias en una forma de carga perezosa para que no se vuele su rendimiento, mientras que en Python solo codifica las inyecciones en las secciones "procesales" (código fuera de clases) de su código
fuente
No he usado Python en varios años, pero diría que tiene más que ver con que sea un lenguaje de tipo dinámico que cualquier otra cosa. Para un ejemplo simple, en Java, si quisiera probar que algo se escribió de manera adecuada, podría usar DI y pasar cualquier PrintStream para capturar el texto que se está escribiendo y verificarlo. Sin embargo, cuando estoy trabajando en Ruby, puedo reemplazar dinámicamente el método 'puso' en STDOUT para hacer la verificación, dejando a DI completamente fuera de la imagen. Si la única razón por la que estoy creando una abstracción es para probar la clase que la está usando (piense en las operaciones del sistema de archivos o el reloj en Java), entonces DI / IoC crea una complejidad innecesaria en la solución.
fuente
En realidad, es bastante fácil escribir un código suficientemente limpio y compacto con DI (me pregunto, será / permanecerá pitónico entonces, pero de todos modos :)), por ejemplo, realmente prefiero esta forma de codificación:
_ _
Sí, esto puede verse como una forma simple de parametrizar funciones / clases, pero hace su trabajo. Entonces, tal vez las baterías incluidas por defecto de Python son suficientes aquí también.
PD: También publiqué un ejemplo más amplio de este enfoque ingenuo al evaluar dinámicamente la lógica booleana simple en Python .
fuente
IoC / DI es un concepto de diseño, pero desafortunadamente a menudo se toma como un concepto que se aplica a ciertos idiomas (o sistemas de escritura). Me encantaría ver que los contenedores de inyección de dependencia se vuelven mucho más populares en Python. Hay Spring, pero ese es un súper marco y parece ser un puerto directo de los conceptos de Java sin mucha consideración por "The Python Way".
Teniendo en cuenta las anotaciones en Python 3, decidí tener un crack en un contenedor de inyección de dependencia con todas las funciones, pero simple: https://github.com/zsims/dic . Se basa en algunos conceptos de un contenedor de inyección de dependencia .NET (que IMO es fantástico si alguna vez estás jugando en ese espacio), pero mutado con conceptos de Python.
fuente
Creo que debido a la naturaleza dinámica de Python, la gente no suele ver la necesidad de otro marco dinámico. Cuando una clase hereda del 'objeto' de nuevo estilo, puede crear una nueva variable dinámicamente ( https://wiki.python.org/moin/NewClassVsClassicClass ).
es decir, en Python simple:
Sin embargo, eche un vistazo a https://github.com/noodleflake/pyioc, esto podría ser lo que está buscando.
es decir, en pyioc
fuente
other.py
línea 1, hay una resolución de dependencia automatizada, pero no lo consideraría como una inyección de dependencia.Respaldo la respuesta "Jörg W Mittag": "La implementación Python de DI / IoC es tan liviana que desaparece por completo".
Para respaldar esta afirmación, eche un vistazo al famoso ejemplo de Martin Fowler portado de Java a Python: Python: Design_Patterns: Inversion_of_Control
Como puede ver en el enlace de arriba, se puede escribir un "Contenedor" en Python en 8 líneas de código:
fuente
My 2cents es que en la mayoría de las aplicaciones de Python no lo necesitas e, incluso si lo necesitaras, es probable que muchos enemigos de Java (y violinistas incompetentes que creen ser desarrolladores) lo consideren algo malo, solo porque es popular en Java .
Un sistema IoC es realmente útil cuando tiene redes complejas de objetos, donde cada objeto puede ser una dependencia para varios otros y, a su vez, ser dependiente de otros objetos. En tal caso, querrá definir todos estos objetos una vez y tener un mecanismo para juntarlos automáticamente, en función de la mayor cantidad posible de reglas implícitas. Si también tiene que definir la configuración de manera simple por el usuario / administrador de la aplicación, esa es una razón adicional para desear un sistema IoC que pueda leer sus componentes de algo así como un simple archivo XML (que sería la configuración).
La aplicación típica de Python es mucho más simple, solo un montón de scripts, sin una arquitectura tan compleja. Personalmente, soy consciente de lo que es realmente un IoC (al contrario de aquellos que escribieron ciertas respuestas aquí) y nunca he sentido la necesidad de hacerlo en mi experiencia limitada en Python (tampoco uso Spring en todas partes, no cuando las ventajas da no justifica su sobrecarga de desarrollo).
Dicho esto, hay situaciones de Python en las que el enfoque de IoC es realmente útil y, de hecho, leí aquí que Django lo usa.
El mismo razonamiento anterior podría aplicarse a la Programación Orientada a Aspectos en el mundo Java, con la diferencia de que el número de casos en los que realmente vale la pena AOP es aún más limitado.
fuente
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- toda una suposiciónaccesorios de pytest todos basados en DI ( fuente )
fuente
Estoy de acuerdo con @Jorg en el punto de que DI / IoC es posible, más fácil y aún más hermoso en Python. Lo que falta son los marcos que lo soportan, pero hay algunas excepciones. Para señalar un par de ejemplos que me vienen a la mente:
Los comentarios de Django le permiten conectar su propia clase de comentarios con su lógica y formularios personalizados. [Más información]
Django le permite usar un objeto de perfil personalizado para adjuntarlo a su modelo de usuario. Esto no es completamente IoC pero es un buen enfoque. Personalmente, me gustaría reemplazar el modelo de usuario del agujero como lo hace el marco de comentarios. [Más información]
fuente
En mi opinión, cosas como la inyección de dependencia son síntomas de un marco rígido y demasiado complejo. Cuando el cuerpo principal del código se vuelve demasiado pesado para cambiarlo fácilmente, tiene que elegir pequeñas partes del mismo, definir interfaces para ellos y luego permitir que las personas cambien el comportamiento a través de los objetos que se conectan a esas interfaces. Eso está muy bien, pero es mejor evitar ese tipo de complejidad en primer lugar.
También es el síntoma de un lenguaje de tipo estático. Cuando la única herramienta que tienes para expresar la abstracción es la herencia, entonces eso es más o menos lo que usas en todas partes. Dicho esto, C ++ es bastante similar, pero nunca captó la fascinación con los Constructores e Interfaces en todas partes que hicieron los desarrolladores de Java. Es fácil volverse demasiado exuberante con el sueño de ser flexible y extensible a costa de escribir demasiado código genérico con pocos beneficios reales . Creo que es una cosa cultural.
Por lo general, creo que las personas de Python están acostumbradas a elegir la herramienta adecuada para el trabajo, que es un todo coherente y simple, en lugar de la Herramienta Una Verdadera (con mil complementos posibles) que puede hacer cualquier cosa, pero ofrece una variedad desconcertante de posibles permutaciones de configuración . Todavía hay partes intercambiables cuando es necesario, pero sin la necesidad del gran formalismo de definir interfaces fijas, debido a la flexibilidad del tipeo de pato y la relativa simplicidad del lenguaje.
fuente
EmailSender
y decido reemplazarlo por unDesktopNotifier
, tengo que ir y editar 12 clases a mano. ¿Y cree que es más simple y limpio que simplemente escribir en unaINotifier
interfaz y dejar que el contenedor resuelva los detalles?A diferencia de la fuerte naturaleza escrita en Java. El comportamiento de tipeo de pato de Python hace que sea muy fácil pasar objetos.
Los desarrolladores de Java se están centrando en la construcción de la estructura de clase y la relación entre los objetos, manteniendo las cosas flexibles. IoC es extremadamente importante para lograr esto.
Los desarrolladores de Python se están centrando en hacer el trabajo. Simplemente conectan las clases cuando lo necesitan. Ni siquiera tienen que preocuparse por el tipo de clase. ¡Mientras pueda graznar, es un pato! Esta naturaleza no deja espacio para IoC.
fuente