¿Por qué IoC / DI no es común en Python?

313

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.

tux21b
fuente
2
Supongo, la misma razón por la que es menos popular en Ruby, mixins incorporados y clases abiertas
Sam Saffron
3
¿Alguna vez has probado Springpython? Ni siquiera funciona como se anuncia. al menos en la parte superior. todo lo demás allí no es muy útil a menos que venga de Java y necesite cierto nivel de comodidad durante la transición.
Tom Willis
66
Tenga cuidado de distinguir entre el uso de DI y el uso de un marco IOC. El primero es un patrón de diseño, el segundo es un marco para ayudar en el uso automatizado del primero.
Doug
Doug, creo que querías decir que DI es la característica de creación que se obtiene al usar el patrón Decorador.
njappboy
44
Me encantaría ver una respuesta que aborde los problemas del mundo real que DI resuelve: administración de por vida, facilidad de prueba, etc. Si hay una manera más pitónica de abordarlos, soy todo oídos.
Josh Noe

Respuestas:

198

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

  1. conecte componentes independientes en una aplicación completa ...
  2. ... en tiempo de ejecución.

Tenemos nombres para "cablear juntos" y "en tiempo de ejecución":

  1. secuencias de comandos
  2. dinámica

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:

Jörg W Mittag
fuente
58
Mientras estoy de acuerdo. El comentario XML es incorrecto. Muchos (al menos los modernos) contenedores IOC usan convenciones (código) sobre la configuración (XML).
Finglas
20
No hay nada que le impida escribir el cableado explícitamente en Java, pero a medida que tiene más y más servicios, las dependencias se vuelven más complejas. Un contenedor DI es como Make: declara las dependencias y el contenedor las inicializa en el orden correcto. Guice es un marco Java DI donde todo está escrito en código Java. Al escribir declarativamente, un contenedor DI también agrega soporte para el procesamiento posterior de las declinaciones antes de la inicialización (por ejemplo, reemplazar los marcadores de posición de propiedad con valores reales)
IttayD
133
"Sin embargo, la implementación de DI / IoC está integrada en el lenguaje y, a menudo, es tan liviana que desaparece por completo". Voto negativo porque esto es categóricamente falso. DI es un patrón donde se pasa una interfaz al constructor. No está integrado en Python.
Doug
146
voto negativo, el cableado conjunto no tiene nada que ver con las secuencias de comandos, DI es un patrón y no es equivalente a las secuencias de comandos
Luxspes
38
No estoy de acuerdo con esto. DI no resuelve la falta de secuencias de comandos dinámicas en lenguajes estáticos. Proporciona un marco para configurar y componer las partes de su aplicación. Una vez escuché a un desarrollador de Ruby decir que DI es innecesario en lenguajes dinámicos. Pero usó Rails ... Rails es solo un gran contenedor DI, que utiliza la convención para determinar qué partes configurar cuando. No necesitaba DI porque Rails resolvió el problema de encontrar las piezas para él.
Brian Genisio
51

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.

TM.
fuente
2
Eso tiene sentido. Si quiero cambiar una implementación en Python, simplemente importo desde una ubicación diferente con el mismo nombre. Pero ahora estoy pensando si también es posible al revés definiendo una MyClassInstancesclase para cada uno MyClassen Java, que contiene solo instancias estáticas completamente inicializadas. Eso estaría conectado: D
tux21b
2
Y otra idea: proporcionar una forma de cambiar tales importaciones en Python permitiría reemplazar las implementaciones fácilmente sin tocar todos los archivos de Python. En lugar de from framework.auth.user import User esto, podría ser mejor escribir User = 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 la Userimplementación sin tocar el marco.
tux21b
14
Simplificando demasiado, responda, en la vida real, rara vez necesita solo "un singleton", necesita controlar el alcance (puede necesitar un hilo singleton local, o un singleton de sesión, etc.), esto me hace pensar que el tipo de problemas resuelto en Python no son el tipo de problemas del mundo real en realidad resueltos en un entorno empresarial
Luxspes
3
En realidad, DI se trata de poder probar y desacoplar dependencias de código. Además, la función de importación es similar a las importaciones estáticas en Java, lo que me permite importar una sola instancia de un objeto.
Richard Warburton el
1
"Puede obtener una especie de" singleton "de forma gratuita, simplemente importándolo desde un módulo". Se puede hacer fácilmente en Java al declarar un campo de instancia estático y establecerlo en un valor. Esto no es un sol
ggranum
45

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:

# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': REDIS_URL + '/1',
    },
    'local': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'snowflake',
    }
}

Django Rest Framework utiliza DI en gran medida:

class FooView(APIView):
    # The "injected" dependencies:
    permission_classes = (IsAuthenticated, )
    throttle_classes = (ScopedRateThrottle, )
    parser_classes = (parsers.FormParser, parsers.JSONParser, parsers.MultiPartParser)
    renderer_classes = (renderers.JSONRenderer,)

    def get(self, request, *args, **kwargs):
        pass

    def post(self, request, *args, **kwargs):
        pass

Déjame recordar ( fuente ):

La "inyección de dependencia" es un término de 25 dólares para un concepto de 5 centavos. [...] La inyección de dependencia significa dar a un objeto sus variables de instancia. [...]

Max Malysh
fuente
8
+1. Así poner. Siendo un programador de Python, estaba completamente desconcertado por una presentación de entrevista completa sobre marcos DI en C #. Me tomó un tiempo darme cuenta de que ya lo hacía todo el tiempo en las aplicaciones de Flask sin siquiera pensarlo porque no necesitas un marco. Para alguien que no sabe nada más allá de C # / Java, la pregunta tiene sentido. Para los programadores de lenguaje tipo pato es natural y, como usted dice, "término de 25 dólares para un concepto de 5 centavos".
Samuel Harmer
55
err ... esto no es inyección de dependencia ya que las instancias ( IsAuthenticated, ScopedRateThrottle) son instanciadas por la clase. No se pasan al constructor.
dopatraman
55
IsAuthenticatedy ScopedRateThrottleno 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. IsAuthenticatedy ScopedRateThrottleson las dependencias; se inyectan en el FooView. No importa cuándo o cómo se haga esto. Python no es Java, por lo que hay diferentes formas de implementar esto.
Max Malysh
3
@MaxMalysh Estoy de acuerdo con dopatraman en este caso. Esto ni siquiera es IoC ya que la clase en sí misma tiene dependencias "codificadas" a una clase específica. En IoC, la dependencia se debe proporcionar en lugar de codificar. Además de eso, en Inyección de dependencias, tendrá una entidad responsable de administrar los ciclos de vida de cada servicio e inyectarlos cuando sea el caso. La solución proporcionada en ninguno de esos.
Ricardo Alves
3
@alex No, no necesita cambiar su código para usar otro procesador. Incluso puede utilizar varios procesadores al mismo tiempo: 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.
Max Malysh
35

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:

if config_dbms_name == 'postgresql':
    import psycopg
    self.database_interface = psycopg
elif config_dbms_name == 'mysql':
    ...

El código posterior puede crear una interfaz de base de datos escribiendo:

my_db_connection = self.database_interface()
# Do stuff with database.

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.

Daniel Newby
fuente
44
Lo que llamas código es en realidad la parte del cableado. Ese sería el XML de su marco ioc. En realidad, podría escribirse simplemente como import psycopg2 as database_interface. Pon esa línea en a injections.pyet voilà.
espectros
29
Erm. Lo que estás haciendo allí es prácticamente el libro de texto imprescindible Daniel.
Shayne
Definitivamente es un código imperativo, pero es un poco funcional porque usa un invocable como valor.
Jeremy
55
¿No son solo funciones de primera clase? en.wikipedia.org/wiki/First-class_function El hecho de que los tenga y los use no hace que su código sea funcional. Hay bastantes efectos secundarios que suceden aquí (como el cambio self.database_interface), que grita imperativo.
hjc1710
15

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

jhonatan teixeira
fuente
todavía extraña que un IoC / DI conecte los objetos automáticamente. No es mucho poder hacerlo en tiempo de ejecución (Java puede hacerlo a través de la reflexión de todos modos), es que el marco se encarga de eso y no es necesario hacerlo explícitamente. Tener secciones de procedimiento tampoco es relevante, nada impide que uno escriba una aplicación completamente de procedimiento en Java, al usar clases como meros contenedores de subrutinas y funciones estáticas, sin usar las características de OOP.
zakmck
@zakmck: la sección "procesal" de Python aquí no se trata realmente de escribir código de procedimiento. Lo que hace que la sección "procesal" de Python sea diferente a los lenguajes estáticos es la capacidad de poner código de procedimiento en un cuerpo de clase, que se ejecuta durante el tiempo de definición de clase, y poner declaraciones de importación dentro de la declaración if, y crear una fábrica de clases simplemente definiendo clases dentro de un método de fábrica. Estas son cosas que realmente no puedes hacer en lenguajes estáticos, y que resuelven la mayoría de los problemas que IOC / DI intentó resolver. La metaprogramación en Python a menudo se parece al código Python normal.
Mentira Ryan
@LieRyan, puede hacerlo con reflexión o, si lo necesita con frecuencia o en tiempo de ejecución, puede llamar al lenguaje estático desde otro idioma como Groovy (que está diseñado para jugar fácilmente con Java), o incluso Python. Sin embargo, eso tiene poco que ver con los marcos IoC / DI, ya que su propósito es hacer la mayor parte del cableado de objetos de procedimiento por usted, automáticamente, aprovechando solo las definiciones. Lamentablemente, la mayoría de las respuestas por este medio pierden este punto.
zakmck
12

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.

bcarlso
fuente
3
Nunca deja de sorprenderme que las personas dispuestas a cambiar la forma en que funciona un sistema para probar que funcionó. Ahora debe comprobar que sus pruebas no causan efectos secundarios.
Básico
2
habla sobre cambiar el método de poner solo en el alcance de las pruebas, es como un método simulado de objeto inyectado.
dpa
2
@Basic es bastante normal en las pruebas unitarias , en realidad es recomendable hacerlo en estas pruebas ya que no desea contaminar la cobertura de su caso de prueba con más de un bloque de código (el que se está probando). Sin embargo, sería un error hacer eso para las pruebas de integración, ¿tal vez a eso se refiere en su comentario?
samuelgrigolato
1
Para mí, la capacidad de prueba es una preocupación de primera clase. Si un diseño no es comprobable, no es un buen diseño, y no tengo problemas para cambiarlo para hacerlo más comprobable. Tendré que volver a validar que todavía funciona, pero está bien. La capacidad de prueba es una razón perfectamente válida para cambiar el código de la OMI
Carlos Rodríguez
10

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:

def polite(name_str):
    return "dear " + name_str

def rude(name_str):
    return name_str + ", you, moron"

def greet(name_str, call=polite):
    print "Hello, " + call(name_str) + "!"

_ _

>>greet("Peter")
Hello, dear Peter!
>>greet("Jack", rude)
Hello, Jack, you, moron!

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 .

mlvljr
fuente
3
Para casos simples que podrían funcionar, pero imagínense un controlador de blog web simple, que utiliza varios modelos (Publicar, Comentar, Usuario). Si desea que el usuario inyecte su propio modelo de publicación (con un atributo de conteo de vistas adicional para rastrear eso) y su propio modelo de usuario con más información de perfil, etc., todos los parámetros pueden parecer confusos. Además, el usuario también puede querer cambiar el objeto Solicitud, para admitir la sesión del sistema de archivos en lugar de una simple sesión basada en cookies o algo así ... Por lo tanto, terminará con muchos parámetros en breve.
tux21b
1
@ tux21b Bueno, existe una "complejidad esencial" que los usuarios quieren que la aplicación implemente, hay soluciones arquitectónicas (algunas de las cuales no son peores que el resto en términos de desarrollo y posiblemente tiempo de mantenimiento, velocidad de ejecución, etc. ), y existe la capacidad humana de comprender la arquitectura de API y software. Si no hay una solución comprensible para los humanos (no solo entre aquellos que usan (cualquier forma de) DI) ... bueno, ¿quién dijo que todos los problemas tienen solución? Y tener muchos parámetros asignados por defecto (pero intercambiables por elección del usuario) en realidad puede ser suficiente a menudo.
mlvljr
9

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.

zsims
fuente
6

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:

#application.py
class Application(object):
    def __init__(self):
        pass

#main.py
Application.postgres_connection = PostgresConnection()

#other.py
postgres_connection = Application.postgres_connection
db_data = postgres_connection.fetchone()

Sin embargo, eche un vistazo a https://github.com/noodleflake/pyioc, esto podría ser lo que está buscando.

es decir, en pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()
Martin Swanepoel
fuente
2
El hecho de que ambas versiones tengan la misma cantidad de código explica en gran medida por qué el uso de un marco no es muy popular.
espectros
En la other.pylínea 1, hay una resolución de dependencia automatizada, pero no lo consideraría como una inyección de dependencia.
andho
Los localizadores de servicios suelen ser un antipatrón, solo lo dicen.
PmanAce
6

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:

class Container:
    def __init__(self, system_data):
        for component_name, component_class, component_args in system_data:
            if type(component_class) == types.ClassType:
                args = [self.__dict__[arg] for arg in component_args]
                self.__dict__[component_name] = component_class(*args)
            else:
                self.__dict__[component_name] = component_class
Emilmont
fuente
42
Esto está lejos de incluso los contenedores DI más débiles. ¿Dónde está la gestión de por vida, la resolución de dependencia recursiva, la capacidad de burlarse o, en su defecto, la configuración? Esto no es más que una búsqueda de tipo y caché que no es lo mismo que IoC.
Básico
2
Hace años escribí un pequeño marco DI utilizando metaclases como ejercicio. Todo es un archivo único con cero importaciones y pruebas de documentación que lo hacen autoexplicativo. Muestra que las características básicas no son tan difíciles de implementar de una manera que sea incluso "pitónica", pero creo sinceramente que es triste que ninguna solución completa haya tenido una mayor tracción como la que tiene Spring en Java y todos están haciendo arquitecturas de complementos personalizados.
Andrea Ratto
2

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.

zakmck
fuente
¿Existe una URL de referencia para la fuente de información donde django usa IoC?
Sajuuk
@Sajuuk, he aprendido eso sobre Django en el hilo de esta pregunta, así que no sé, debes preguntar a los otros autores de respuestas.
zakmck
La primera alineación de esta respuesta agrega 0 valor en mi opinión ... Creo que soy capaz de decidir cuándo mi código de Python se beneficiaría de IoC, y no me importa lo que el desarrollador piense que es malo. Valoro el pragmatismo sobre las opiniones sin fundamento.
Mike de Klerk
@MikedeKlerk, mi sugerencia es que es poco probable que algo que es desconocido (como lo prueban muchas respuestas) y víctima de prejuicios sea popular, no importa cuán objetivos y bien informados sean unos pocos como usted. Y, por supuesto, no estoy seguro de que esta sea una razón por la que no ve muchos usos de IoC en Python, creo que la razón principal es que las aplicaciones de baja / media competencia no los necesitan.
zakmck
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.- toda una suposición
hyankov
1

accesorios de pytest todos basados ​​en DI ( fuente )

Meng Zhao
fuente
-1

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]

santiagobasulto
fuente
-3

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.

Kylotan
fuente
44
No es tanto el marco como el lenguaje en sí. Para crear el tipo de flexibilidad que disfrutan los lenguajes de mecanografía, los lenguajes mecanografiados estáticamente necesitan marcos y reglas muy sofisticados. DI es una de esas reglas. La gente de Python no lo piensa dos veces. La gente de Java realmente tiene que trabajar en eso.
S.Lott
66
@ S.Lott: estoy totalmente de acuerdo con usted, excepto que la gente de C ++ parece funcionar sin la explosión de patrones de diseño y arquitectura, a pesar de trabajar con restricciones similares a las de Java. Creo que eso implica una diferencia cultural en la que, al enfrentarse a 2 formas posibles de hacer algo, las personas de Java prefieren extraer otra interfaz para facilitar el patrón de Estrategia, mientras que las personas de C ++ se sumergen y agregan un bool y una declaración if ...
Kylotan
3
@Finglas, así que si tengo una docena de clases y todas uso mi EmailSendery decido reemplazarlo por un DesktopNotifier, tengo que ir y editar 12 clases a mano. ¿Y cree que es más simple y limpio que simplemente escribir en una INotifierinterfaz y dejar que el contenedor resuelva los detalles?
Básico
1
Desafortunadamente, un cierto nivel de complejidad es una realidad que los desarrolladores de software profesionales deben enfrentar. Veo críticas pero no hay soluciones en esta respuesta. ¿Cuál es la solución "pitónica" para este problema? Estoy escribiendo una biblioteca y quiero proporcionar un enlace para iniciar sesión (algo así como la interfaz de registro PSR-3 de PHP). Sé cómo usar los niveles de registro, pero no me importa cómo el programa realmente los informa. ¿Cuál es la forma limpia de permitir que la aplicación cliente inyecte ese detalle de implementación? Nota: otras partes de la aplicación pueden tener implementaciones diferentes de esta interfaz.
Rob
2
Mi pregunta para usted no es cómo utiliza la biblioteca de registro estándar, ni se trata de crear diferentes instancias de una clase de registrador. Mi pregunta es cómo configurar su aplicación para que diferentes partes de su aplicación puedan usar diferentes implementaciones, y no preocuparse por esos detalles (siempre que sepan cómo usar la interfaz). Este es un problema muy real que DI ha resuelto para múltiples aplicaciones PHP en las que he trabajado. Estoy buscando el equivalente a Python. Y sugerir que "simplemente no haga que su aplicación sea tan compleja" no es la respuesta que estoy buscando.
Rob
-5

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.

Jason Ching
fuente
44
Todavía necesitas encontrar algo que grazne.
andho