Pourquoi l'IoC / DI n'est-il pas courant en Python?

313

Dans Java IoC / DI est une pratique très courante qui est largement utilisée dans les applications Web, presque tous les cadres disponibles et Java EE. D'un autre côté, il existe également de nombreuses grandes applications Web Python, mais à côté de Zope (dont j'ai entendu dire qu'il devrait être vraiment horrible à coder), l'IoC ne semble pas être très courant dans le monde Python. (Veuillez citer quelques exemples si vous pensez que je me trompe).

Il existe bien sûr plusieurs clones de frameworks Java IoC populaires disponibles pour Python, springpython par exemple. Mais aucun d'entre eux ne semble s'habituer pratiquement. Au moins, je ne suis jamais tombé sur une application Web basée sur Django ou sqlalchemy + <insert your favorite wsgi toolkit here>qui utilise quelque chose comme ça.

À mon avis, l'IoC présente des avantages raisonnables et faciliterait le remplacement du modèle utilisateur django-default-user par exemple, mais l'utilisation extensive des classes d'interface et de l'IoC en Python semble un peu étrange et non pas «pythonique». Mais peut-être que quelqu'un a une meilleure explication, pourquoi l'IoC n'est pas largement utilisé en Python.

tux21b
la source
2
Ma conjecture, même raison qu'il est moins populaire en Ruby, mixins intégrés et classes ouvertes
Sam Saffron
3
vous avez déjà essayé springpython? cela ne fonctionne même pas comme annoncé. au moins dans la partie aop. tout le reste n'est pas très utile sauf si vous venez de Java et avez besoin d'un certain niveau de confort pendant la transition.
Tom Willis
6
Veuillez prendre soin de faire la distinction entre l'utilisation de DI et l'utilisation d'un cadre IOC. Le premier est un modèle de conception, le second est un cadre pour aider à l'utilisation automatisée du premier.
Doug
Doug, je pense que vous vouliez dire que DI est la fonction de création obtenue en utilisant le motif Décorateur.
njappboy
4
Je serais ravi de voir une réponse qui résout les problèmes du monde réel que DI résout: gestion de la durée de vie, facilité de stubbing de test, etc.
Josh Noe

Réponses:

198

Je ne pense pas vraiment que DI / IoC soient si rares en Python. Ce qui est rare, cependant, ce sont les cadres / conteneurs DI / IoC .

Pensez-y: que fait un conteneur DI? Il vous permet de

  1. câbler des composants indépendants dans une application complète ...
  2. ... lors de l'exécution.

Nous avons des noms pour «câbler ensemble» et «à l'exécution»:

  1. scripting
  2. dynamique

Ainsi, un conteneur DI n'est rien d'autre qu'un interprète pour un langage de script dynamique. En fait, permettez-moi de reformuler cela: un conteneur DI Java / .NET typique n'est rien d'autre qu'un interpréteur de merde pour un langage de script dynamique vraiment mauvais avec une syntaxe bout à bout, parfois basée sur XML.

Lorsque vous programmez en Python, pourquoi voudriez-vous utiliser un langage de script laid et mauvais alors que vous avez un beau langage de script brillant à votre disposition? En fait, c'est une question plus générale: lorsque vous programmez dans à peu près n'importe quel langage, pourquoi voudriez-vous utiliser un langage de script laid et mauvais alors que vous avez Jython et IronPython à votre disposition?

Donc, pour récapituler: la pratique de DI / IoC est tout aussi importante en Python qu'en Java, pour exactement les mêmes raisons. La mise en œuvre de DI / IoC est cependant intégrée au langage et souvent si légère qu'elle disparaît complètement.

(Voici une brève mise de côté pour une analogie: dans l'assemblage, un appel de sous-programme est une affaire assez importante - vous devez enregistrer vos variables et registres locaux en mémoire, enregistrer votre adresse de retour quelque part, changer le pointeur d'instruction en sous-programme que vous appelez, faites en sorte qu'il revienne d'une manière ou d'une autre dans votre sous-programme lorsqu'il est terminé, placez les arguments quelque part où l'appelé peut les trouver, etc. IOW: dans l'assemblage, "l'appel de sous-programme" est un modèle de conception, et avant il y avait des langues comme Fortran qui avait des appels de sous-programmes intégrés, les gens construisaient leurs propres "cadres de sous-programmes". Diriez-vous que les appels de sous-programmes sont "rares" en Python, juste parce que vous n'utilisez pas de cadres de sous-programmes?)

BTW: pour un exemple de ce qu'il ressemble à prendre DI à sa conclusion logique, jetez un oeil à Gilad Bracha de novlangue Langage de programmation et de ses écrits sur le sujet:

Jörg W Mittag
la source
58
Bien que je sois d'accord. Le commentaire XML est faux. De nombreux conteneurs IOC (au moins modernes) utilisent la convention (code) sur la configuration (XML).
Finglas
20
Rien ne vous empêche d'écrire explicitement le câblage en Java, mais comme vous avez de plus en plus de services, les dépendances deviennent plus complexes. Un conteneur DI est comme Make: vous déclarez les dépendances et le conteneur les initialise dans le bon ordre. Guice est un framework Java DI où tout est écrit en code Java. En écrivant de manière déclarative, un conteneur DI ajoute également la prise en charge du post-traitement des décalages avant l'initialisation (par exemple, remplacez les espaces réservés de propriété par des valeurs réelles)
IttayD
133
"L'implémentation de DI / IoC est cependant intégrée au langage et souvent si légère qu'elle disparaît complètement." Vote contre parce que cela est catégoriquement faux. DI est un modèle dans lequel une interface est transmise au constructeur. Il n'est pas intégré en python.
Doug
146
downvote, le câblage n'a rien à voir avec les scripts, DI est un modèle, et ce n'est pas équivalent à des scripts
Luxspes
38
Je suis en désaccord avec cela. DI ne résout pas le manque de script dynamique dans les langages statiques. Il fournit un cadre pour configurer et composer les composants de votre application. J'ai entendu un jour un développeur Ruby dire que DI n'était pas nécessaire dans les langages dynamiques. Mais il a utilisé Rails ... Rails est juste un gros conteneur DI de toutes sortes, qui utilise la convention pour déterminer quelles parties configurer quand. Il n'avait pas besoin de DI parce que Rails avait résolu le problème de trouver les pièces pour lui.
Brian Genisio
51

Cela dépend en partie du fonctionnement du système de modules en Python. Vous pouvez obtenir une sorte de "singleton" gratuitement, simplement en l'important depuis un module. Définissez une instance réelle d'un objet dans un module, puis tout code client peut l'importer et obtenir réellement un objet fonctionnel, entièrement construit / rempli.

Cela contraste avec Java, où vous n'importez pas d'instances réelles d'objets. Cela signifie que vous devez toujours les instancier vous-même (ou utiliser une sorte d'approche de type IoC / DI). Vous pouvez atténuer les tracas d'avoir à tout instancier vous-même en ayant des méthodes d'usine statiques (ou des classes d'usine réelles), mais vous encourez toujours les frais généraux liés à la création de nouvelles à chaque fois.

TM.
la source
2
Ça a du sens. Si je souhaite modifier une implémentation en Python, j'importe simplement à partir d'un emplacement différent en utilisant le même nom. Mais maintenant, je pense que c'est également possible dans l'autre sens en définissant une MyClassInstancesclasse pour chacune MyClassen Java, qui ne contient que des instances statiques entièrement initialisées. Ce serait câblé: D
tux21b
2
Et une autre idée: fournir un moyen de changer ces importations en python permettrait de remplacer facilement les implémentations sans toucher à tous les fichiers python. Au lieu de cela, from framework.auth.user import User il pourrait être préférable d'écrire User = lookup('UserImplentation', 'framework.auth.user.User')(le 2e paramètre peut être une valeur par défaut) à l'intérieur du cadre. Les utilisateurs du framework pourront alors remplacer / spécialiser l' Userimplémentation sans toucher au framework.
tux21b
14
Trop simplifier, répondre, dans la vie réelle, vous avez rarement besoin d'un "singleton", vous devez contrôler la portée (vous pourriez avoir besoin d'un thread local singleton, ou d'une session singleton, et ainsi de suite), cela me fait penser que le genre de problèmes résolus en Python ne sont pas le genre de problèmes du monde réel réellement résolus en entreprise
Luxspes
3
En fait, DI consiste à pouvoir tester et découpler les dépendances de code. De plus, la fonction d'importation est similaire aux importations statiques en Java, ce qui me permet d'importer une seule instance d'un objet.
Richard Warburton
1
"Vous pouvez obtenir une sorte de" singleton "gratuitement, simplement en l'important à partir d'un module." Peut être fait facilement en Java en déclarant un champ d'instance statique et en le définissant sur une valeur. Ce n'est pas un sol
ggranum
45

IoC et DI sont très courants dans le code Python mature. Vous n'avez simplement pas besoin d'un framework pour implémenter DI grâce au typage canard.

Le meilleur exemple est de savoir comment configurer une application Django en utilisant 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 utilise fortement DI:

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

Permettez-moi de rappeler ( source ):

"Injection de dépendance" est un terme de 25 dollars pour un concept de 5 cents. [...] L'injection de dépendance signifie donner à un objet ses variables d'instance. [...].

Max Malysh
la source
8
+1. Bien placé. En tant que programmeur Python, j'ai été complètement déconcerté par toute une présentation d'entrevue sur les cadres DI en C #. Cela m'a pris du temps pour réaliser que je l'avais déjà fait tout le temps dans les applications Flask sans même y penser parce que vous n'avez pas besoin d'un framework. Pour quelqu'un qui ne sait rien au-delà de C # / Java, la question est logique. Pour les programmeurs de langage de type canard, c'est tout simplement naturel et comme vous le dites, "terme de 25 dollars pour un concept de 5 cents".
Samuel Harmer
5
euh ... ce n'est pas une injection de dépendance car les instances ( IsAuthenticated, ScopedRateThrottle) sont instanciées par la classe. Ils ne sont pas transmis au constructeur.
dopatraman
5
IsAuthenticatedet ScopedRateThrottlene sont pas des instances, ce sont des classes. Ils sont instanciés lors de la construction d'un FooView (en fait, lorsque le FooView traite une demande). Quoi qu'il en soit, ce n'est qu'un détail de mise en œuvre. IsAuthenticatedet ScopedRateThrottlesont les dépendances; ils sont injectés dans le FooView. Peu importe quand ou comment cela se fait. Python n'est pas Java, il existe donc différentes manières de l'implémenter.
Max Malysh
3
@MaxMalysh Je suis d'accord avec dopatraman sur celui-ci. Ce n'est même pas l'IoC car la classe elle-même a des dépendances "codées en dur" à une classe spécifique. Dans IoC, la dépendance doit être fournie au lieu d'être codée en dur. En plus de cela, dans l'Injection de dépendances, vous disposerez d'une entité chargée de gérer les cycles de vie de chaque service et de les injecter le cas échéant. La solution n'a fourni aucun d'entre eux.
Ricardo Alves
3
@alex Non, vous n'avez pas besoin de modifier votre code pour utiliser un autre moteur de rendu. Vous pouvez même utiliser simultanément plusieurs moteurs de rendu: renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer). La moquerie est aussi simple que @unittest.patch('myapp.views.FooView.permission_classes'). Un besoin désespéré de «passer quelque chose» est une conséquence de la «manière Java de faire les choses» car Java est un langage compilé et typé statiquement dépourvu de solides capacités de métaprogrammation.
Max Malysh
35

Django fait un grand usage de l'inversion de contrôle. Par exemple, le serveur de base de données est sélectionné par le fichier de configuration, puis la structure fournit des instances d'encapsuleur de base de données appropriées aux clients de base de données.

La différence est que Python a des types de première classe. Les types de données, y compris les classes, sont eux-mêmes des objets. Si vous voulez que quelque chose utilise une classe particulière, nommez simplement la classe. Par exemple:

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

Un code ultérieur peut ensuite créer une interface de base de données en écrivant:

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

Au lieu des fonctions d'usine passe-partout dont Java et C ++ ont besoin, Python le fait avec une ou deux lignes de code ordinaire. C'est la force de la programmation fonctionnelle versus impérative.

Daniel Newby
la source
4
Ce que vous appelez le code est en fait la partie câblage. Ce serait le XML de votre framework ioc. Il pourrait en fait être écrit simplement comme import psycopg2 as database_interface. Mettez cette ligne dans un injections.pyet voilà.
spectras
29
Euh. Ce que vous faites là-bas est à peu près impératif Daniel.
Shayne
C'est certainement du code impératif, mais c'est un peu fonctionnel car il utilise un callable comme valeur.
Jeremy
5
N'est-ce pas seulement des fonctions de première classe? en.wikipedia.org/wiki/First-class_function Ce n'est pas parce que vous les avez et les utilisez que votre code est fonctionnel. Il y a un certain nombre d'effets secondaires qui se produisent ici (tels que le changement self.database_interface), qui crient impérativement.
hjc1710
15

Il semble que les gens ne comprennent plus vraiment ce que signifie l'injection de dépendance et l'inversion du contrôle.

La pratique de l'inversion du contrôle consiste à avoir des classes ou des fonctions qui dépendent d'autres classes ou fonctions, mais au lieu de créer les instances dans la classe de code de fonction, il est préférable de les recevoir en tant que paramètre, de sorte qu'un couplage lâche peut être archivé. Cela présente de nombreux avantages comme une plus grande testabilité et pour archiver le principe de substitution de liskov.

Vous voyez, en travaillant avec des interfaces et des injections, votre code devient plus maintenable, car vous pouvez changer le comportement facilement, car vous n'aurez pas à réécrire une seule ligne de code (peut-être une ligne ou deux sur la configuration DI) de votre pour changer son comportement, car les classes qui implémentent l'interface que votre classe attend peuvent varier indépendamment tant qu'elles suivent l'interface. L'une des meilleures stratégies pour garder le code découplé et facile à maintenir est de suivre au moins les principes de responsabilité unique, de substitution et d'inversion de dépendance.

À quoi sert une bibliothèque DI si vous pouvez instancier un objet vous-même dans un package et l'importer pour l'injecter vous-même? La réponse choisie est juste, car java n'a pas de sections procédurales (code en dehors des classes), tout ce qui va dans les fichiers XML de configuration ennuyeux, d'où le besoin d'une classe pour instancier et injecter des dépendances sur une mode de chargement paresseux afin de ne pas souffler vos performances, alors qu'en python vous codez juste les injections sur les sections "procédurales" (code hors classes) de votre code

jhonatan teixeira
la source
vous manquez toujours qu'un IoC / DI relie les objets automatiquement. Ce n'est pas beaucoup de pouvoir le faire à l'exécution (Java peut le faire via la réflexion de toute façon), c'est que le framework s'en occupe et vous n'avez pas besoin de le faire explicitement. Avoir des sections procédurales n'est pas pertinent non plus, rien n'empêche d'écrire une application entièrement procédurale en Java, en utilisant des classes comme de simples conteneurs de sous-programmes et de fonctions statiques, sans utiliser du tout de fonctionnalités OOP.
zakmck
@zakmck: la section "procédurale" de Python ne concerne pas vraiment l'écriture de code procédural. Ce qui rend la section "procédurale" de Python différente des langages statiques, c'est la possibilité de mettre du code procédural dans un corps de classe, qui s'exécute pendant le temps de définition de classe, et de placer des instructions d'importation dans l'instruction if, et de créer une fabrique de classes simplement en définissant des classes à l'intérieur d'une méthode d'usine. Ce sont des choses que vous ne pouvez pas vraiment faire dans des langages statiques et qui résolvent la plupart des problèmes que l'IOC / DI a essayé de résoudre. La métaprogrammation en Python ressemble souvent au code Python standard.
Lie Ryan
@LieRyan, vous pouvez le faire avec réflexion, ou, si vous en avez besoin souvent ou au moment de l'exécution, vous pouvez appeler le langage statique à partir d'un autre langage comme Groovy (qui est conçu pour jouer facilement avec Java), ou même Python lui-même. Pourtant, cela n'a pas grand-chose à voir avec les frameworks IoC / DI, car leur objectif est de faire la plupart du câblage d'objet procédural pour vous, automatiquement, en s'appuyant uniquement sur les définitions. Malheureusement, la plupart des réponses aux présentes manquent ce point.
zakmck
12

Je n'ai pas utilisé Python depuis plusieurs années, mais je dirais que cela a plus à voir avec le fait qu'il s'agit d'un langage typé dynamiquement qu'autre chose. Pour un exemple simple, en Java, si je voulais tester que quelque chose était écrit correctement, je pouvais utiliser DI et passer n'importe quel PrintStream pour capturer le texte en cours d'écriture et le vérifier. Cependant, lorsque je travaille dans Ruby, je peux remplacer dynamiquement la méthode «met» sur STDOUT pour faire la vérification, laissant DI complètement hors de l'image. Si la seule raison pour laquelle je crée une abstraction est de tester la classe qui l'utilise (pensez aux opérations du système de fichiers ou à l'horloge en Java), alors DI / IoC crée une complexité inutile dans la solution.

bcarlso
la source
3
Cela ne cesse de m'étonner que les gens désireux de changer le fonctionnement d'un système pour tester son fonctionnement. Vous devez maintenant vérifier que vos tests ne provoquent pas d'effets secondaires.
Basic
2
il parle de changer la méthode met uniquement dans la portée des tests, c'est comme la méthode simulée de l'objet injecté.
dpa
2
@Basic c'est assez normal dans les tests unitaires , en fait il est conseillé de le faire dans ces tests car vous ne voulez pas polluer la couverture de votre cas de test avec plus d'un bloc de code (celui qui est testé). Ce serait une erreur de le faire pour les tests d'intégration, c'est peut-être à cela que vous faites référence dans votre commentaire?
samuelgrigolato
1
Pour moi, la testabilité est une préoccupation de première classe. Si un design n'est pas testable, ce n'est pas un bon design, et je n'ai aucun problème à le changer pour le rendre plus testable. Je vais devoir revalider que cela fonctionne toujours, mais ça va. La testabilité est une raison parfaitement valable pour changer le code IMO
Carlos Rodriguez
10

En fait, il est assez facile d'écrire du code suffisamment propre et compact avec DI (je me demande, sera-t-il / restera- t-il alors pythonique , mais de toute façon :)), par exemple, je préfère en fait cette façon de coder:

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!

Oui, cela peut être considéré comme une simple forme de paramétrage des fonctions / classes, mais il fait son travail. Donc, peut-être que les batteries incluses par défaut de Python sont suffisantes ici aussi.

PS J'ai également publié un exemple plus large de cette approche naïve lors de l' évaluation dynamique d'une logique booléenne simple en Python .

mlvljr
la source
3
Pour les cas simples qui pourraient fonctionner, mais imaginez simplement un simple contrôleur de blog Web, qui utilise différents modèles (publication, commentaire, utilisateur). Si vous voulez que l'utilisateur injecte son propre modèle Post (avec un attribut viewcount supplémentaire pour suivre cela), et son propre modèle User avec plus d'informations de profil et ainsi de suite, tous les paramètres peuvent sembler déroutants. De plus, l'utilisateur peut également vouloir changer l'objet Request, pour prendre en charge la session du système de fichiers au lieu d'une simple session basée sur les cookies ou quelque chose comme ça ... Donc, vous vous retrouverez avec beaucoup de paramètres sous peu.
tux21b
1
@ tux21b Eh bien, il y a une "complexité essentielle" que les utilisateurs veulent que l'application implémente, il existe des solutions architecturales (dont certaines ne sont pas pires que les autres en termes de développement et éventuellement de temps de maintenance, de vitesse d'exécution, etc. ), et il y a une capacité humaine à comprendre l'API et l'architecture logicielle. S'il n'y a aucune solution compréhensible par l'homme (pas seulement parmi ceux qui utilisent (n'importe quelle forme de) DI) ... eh bien, qui a dit que tous les problèmes étaient résolubles? Et avoir beaucoup de paramètres assignés par défaut (mais échangeables au choix de l'utilisateur) peut en fait suffire souvent.
mlvljr
9

IoC / DI est un concept de conception, mais malheureusement, il est souvent considéré comme un concept qui s'applique à certains langages (ou systèmes de frappe). J'adorerais voir les conteneurs d'injection de dépendance devenir beaucoup plus populaires en Python. Il y a Spring, mais c'est un super-framework et semble être un port direct des concepts Java sans beaucoup de considération pour "The Python Way".

Compte tenu des annotations dans Python 3, j'ai décidé d'avoir une fissure dans un conteneur d'injection de dépendance complet, mais simple: https://github.com/zsims/dic . Il est basé sur certains concepts d'un conteneur d'injection de dépendances .NET (ce que l'OMI est fantastique si vous jouez jamais dans cet espace), mais a muté avec les concepts Python.

zsims
la source
6

Je pense qu'en raison de la nature dynamique de python, les gens ne voient pas souvent la nécessité d'un autre cadre dynamique. Lorsqu'une classe hérite de l '«objet» de nouveau style, vous pouvez créer une nouvelle variable dynamiquement ( https://wiki.python.org/moin/NewClassVsClassicClass ).

c'est à dire 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()

Cependant, jetez un œil à https://github.com/noodleflake/pyioc, c'est peut-être ce que vous recherchez.

ie dans 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
la source
2
Le fait même que les deux versions prennent la même quantité de code explique en grande partie pourquoi l'utilisation d'un framework n'est pas très populaire.
spectras
À la other.pyligne 1, il existe une résolution de dépendance automatisée, mais ne compterait pas cela comme une injection de dépendance.
andho
Les localisateurs de services sont généralement un anti-modèle, disons simplement.
PmanAce
6

Je soutiens la réponse de "Jörg W Mittag": "L'implémentation Python de DI / IoC est si légère qu'elle disparaît complètement".

Pour sauvegarder cette déclaration, jetez un œil au célèbre exemple de Martin Fowler porté de Java vers Python: Python: Design_Patterns: Inversion_of_Control

Comme vous pouvez le voir sur le lien ci-dessus, un "Container" en Python peut être écrit en 8 lignes de code:

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
la source
42
Cela est bien en deçà des récipients DI les plus faibles. Où est la gestion de la durée de vie, la résolution récursive des dépendances, la possibilité de se moquer ou - à défaut - la configuration? Ce n'est rien de plus qu'une recherche de type et un cache qui n'est pas la même chose que l'IoC.
Basic
2
Il y a des années, j'ai écrit un petit framework DI utilisant des métaclasses comme exercice. Le tout est un fichier unique avec zéro importation et doctests qui le rendent auto-explicatif. Cela montre que les fonctionnalités de base ne sont pas si difficiles à implémenter d'une manière qui soit même "pythonique", mais je pense sincèrement que c'est triste qu'aucune solution complète n'ait obtenu une traction majeure comme Spring en Java et tout le monde fait des architectures de plugins personnalisées.
Andrea Ratto
2

Mon 2cents est que dans la plupart des applications Python, vous n'en avez pas besoin et, même si vous en avez besoin, il est probable que de nombreux haineux Java (et des violonistes incompétents qui croient être des développeurs) le considèrent comme quelque chose de mauvais, simplement parce qu'il est populaire en Java .

Un système IoC est en fait utile lorsque vous avez des réseaux complexes d'objets, où chaque objet peut être une dépendance pour plusieurs autres et, à son tour, être lui-même dépendant d'autres objets. Dans un tel cas, vous souhaiterez définir tous ces objets une fois et disposer d'un mécanisme pour les assembler automatiquement, en fonction du plus grand nombre de règles implicites possible. Si vous avez également une configuration à définir de manière simple par l'utilisateur / l'administrateur de l'application, c'est une raison supplémentaire de désirer un système IoC capable de lire ses composants à partir de quelque chose comme un simple fichier XML (qui serait la configuration).

L'application Python typique est beaucoup plus simple, juste un tas de scripts, sans une architecture aussi complexe. Personnellement, je suis conscient de ce qu'est réellement un IoC (contrairement à ceux qui ont écrit certaines réponses ici) et je n'en ai jamais ressenti le besoin dans mon expérience Python limitée (aussi je n'utilise pas Spring partout, pas quand les avantages il donne ne justifie pas ses frais généraux de développement).

Cela dit, il existe des situations Python où l'approche IoC est réellement utile et, en fait, je lis ici que Django l'utilise.

Le même raisonnement ci-dessus pourrait être appliqué à la programmation orientée aspect dans le monde Java, à la différence près que le nombre de cas où l'AOP vaut vraiment la peine est encore plus limité.

zakmck
la source
Existe-t-il une URL de référence vers la source d'informations où Django utilise l'IoC?
Sajuuk
@Sajuuk, j'ai appris cela sur Django sur le fil de cette question, donc je ne sais pas, vous devriez demander aux autres auteurs de réponse.
zakmck
La première alinea de cette réponse ajoute 0 valeur à mon avis ... Je pense que je suis capable de décider quand mon code python bénéficierait de l'IoC, et je me fiche de ce que le développeur pense de ce qui est mauvais. J'apprécie le pragmatisme plutôt que les opinions non fondées.
Mike de Klerk
@MikedeKlerk ma suggestion est qu'il est peu probable que quelque chose qui est à la fois inconnu (comme le prouvent de nombreuses réponses) et victime de préjugés devienne populaire, aussi objectif et bien informé que soient quelques-uns comme vous. Et bien sûr, je ne suis pas sûr que ce soit une raison pour laquelle vous ne voyez pas beaucoup d'utilisation d'IoC en Python, je pense que la raison principale est que les applications à faible / moyenne compexité n'en ont pas besoin.
zakmck
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture. - toute une hypothèse
hyankov
1

montages pytest tous basés sur DI ( source )

Meng Zhao
la source
-1

Je suis d'accord avec @Jorg sur le fait que DI / IoC est possible, plus facile et encore plus beau en Python. Ce qui manque, ce sont les cadres qui le supportent, mais il y a quelques exceptions. Pour citer quelques exemples qui me viennent à l'esprit:

  • Les commentaires de Django vous permettent de câbler votre propre classe de commentaires avec votre logique et vos formulaires personnalisés. [Plus d'informations]

  • Django vous permet d'utiliser un objet Profil personnalisé pour attacher à votre modèle utilisateur. Ce n'est pas complètement IoC mais c'est une bonne approche. Personnellement, je voudrais remplacer le modèle utilisateur de trou comme le fait le cadre de commentaires. [Plus d'informations]

santiagobasulto
la source
-3

À mon avis, des choses comme l'injection de dépendance sont les symptômes d'un cadre rigide et trop complexe. Lorsque le corps principal du code devient beaucoup trop lourd pour être modifié facilement, vous devez en choisir de petites parties, définir des interfaces pour eux, puis permettre aux utilisateurs de changer de comportement via les objets qui se connectent à ces interfaces. C'est bien beau, mais il vaut mieux éviter ce genre de complexité en premier lieu.

C'est aussi le symptôme d'un langage de type statique. Lorsque le seul outil dont vous disposez pour exprimer l'abstraction est l'héritage, c'est à peu près ce que vous utilisez partout. Cela dit, C ++ est assez similaire mais n'a jamais attiré la fascination des constructeurs et des interfaces partout où les développeurs Java l'ont fait. Il est facile de devenir exubérant avec le rêve d'être flexible et extensible au prix d'écrire beaucoup trop de code générique avec peu d'avantages réels . Je pense que c'est une chose culturelle.

En général, je pense que les gens de Python sont habitués à choisir le bon outil pour le travail, qui est un ensemble cohérent et simple, plutôt que le One True Tool (avec mille plugins possibles) qui peut faire n'importe quoi mais offre un tableau ahurissant de permutations de configuration possibles . Il y a encore des parties interchangeables là où cela est nécessaire, mais sans avoir besoin du grand formalisme de définition des interfaces fixes, en raison de la flexibilité du typage canard et de la relative simplicité du langage.

Kylotan
la source
4
Ce n'est pas tant le cadre que le langage lui-même. Pour créer le type de flexibilité dont bénéficient les langages de type canard, les langages typés statiquement ont besoin de cadres et de règles très sophistiqués. DI est l'une de ces règles. Les gens de Python n'y réfléchissent pas. Les gens de Java doivent vraiment y travailler.
S.Lott
6
@ S.Lott - Je suis totalement d'accord avec vous, sauf que les gens en C ++ semblent s'en tirer sans l'explosion des modèles de conception et d'architecture, malgré le fait de travailler avec des restrictions similaires à celles de Java. Je pense que cela implique une différence culturelle où, face à 2 façons possibles de faire quelque chose, les gens Java préfèrent extraire une autre interface pour faciliter le modèle de stratégie tandis que les gens C ++ y plongent et ajoutent un bool et une instruction if ...
Kylotan
3
@Finglas donc si j'ai une douzaine de classes utilisant toutes mon EmailSenderet que je décide de le remplacer par un DesktopNotifier, je dois aller éditer 12 classes à la main. Et vous pensez que c'est plus simple et plus propre que d'écrire sur une INotifierinterface et de laisser le conteneur régler les détails?
Basic
1
Malheureusement, un certain niveau de complexité est une réalité à laquelle les développeurs de logiciels professionnels doivent faire face. Je vois des critiques mais aucune solution dans cette réponse. Quelle est la solution "pythonique" pour ce problème: j'écris une bibliothèque et je veux fournir un hook pour la journalisation (quelque chose comme PSR-3 LoggerInterface de PHP). Je sais comment utiliser les niveaux de journalisation, mais je me fiche de la façon dont le programme les signale. Quelle est la meilleure façon de permettre à l'application cliente d' injecter ces détails d'implémentation. Remarque: d'autres parties de l'application peuvent avoir différentes implémentations de cette interface.
Rob
2
Ma question n'est pas de savoir comment utiliser la bibliothèque de journalisation standard, ni de créer différentes instances d'une classe de journalisation. Ma question est de savoir comment configurer votre application afin que différentes parties de votre application puissent utiliser différentes implémentations, et ne pas se préoccuper de ces détails (à condition qu'ils sachent comment utiliser l'interface). C'est un problème très réel que DI a résolu pour plusieurs applications PHP sur lesquelles j'ai travaillé. Je cherche l'équivalent python. Et suggérer "ne faites pas que votre application soit aussi complexe" n'est pas la réponse que je recherche.
Rob
-5

Contrairement à la nature typée forte de Java. Le comportement de typage de canard de Python facilite la transmission d'objets.

Les développeurs Java se concentrent sur la construction de la structure de classe et la relation entre les objets, tout en gardant les choses flexibles. L'IoC est extrêmement important pour y parvenir.

Les développeurs de Python se concentrent sur la réalisation du travail. Ils câblent les cours quand ils en ont besoin. Ils n'ont même pas à se soucier du type de cours. Tant qu'il peut charlatan, c'est un canard! Cette nature ne laisse aucune place à l'IoC.

Jason Ching
la source
4
Vous devez toujours trouver une chose qui charrie.
andho