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.
Réponses:
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
Nous avons des noms pour «câbler ensemble» et «à l'exécution»:
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:
la source
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.
la source
MyClassInstances
classe pour chacuneMyClass
en Java, qui ne contient que des instances statiques entièrement initialisées. Ce serait câblé: Dfrom framework.auth.user import User
il pourrait être préférable d'écrireUser = 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'User
implémentation sans toucher au framework.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
:Django Rest Framework utilise fortement DI:
Permettez-moi de rappeler ( source ):
la source
IsAuthenticated
,ScopedRateThrottle
) sont instanciées par la classe. Ils ne sont pas transmis au constructeur.IsAuthenticated
etScopedRateThrottle
ne 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.IsAuthenticated
etScopedRateThrottle
sont les dépendances; ils sont injectés dans leFooView
. Peu importe quand ou comment cela se fait. Python n'est pas Java, il existe donc différentes manières de l'implémenter.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.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:
Un code ultérieur peut ensuite créer une interface de base de données en écrivant:
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.
la source
import psycopg2 as database_interface
. Mettez cette ligne dans uninjections.py
et voilà.self.database_interface
), qui crient impérativement.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
la source
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.
la source
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:
_
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 .
la source
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.
la source
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:
Cependant, jetez un œil à https://github.com/noodleflake/pyioc, c'est peut-être ce que vous recherchez.
ie dans pyioc
la source
other.py
ligne 1, il existe une résolution de dépendance automatisée, mais ne compterait pas cela comme une injection de dépendance.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:
la source
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é.
la source
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- toute une hypothèsemontages pytest tous basés sur DI ( source )
la source
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]
la source
À 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.
la source
EmailSender
et que je décide de le remplacer par unDesktopNotifier
, je dois aller éditer 12 classes à la main. Et vous pensez que c'est plus simple et plus propre que d'écrire sur uneINotifier
interface et de laisser le conteneur régler les détails?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.
la source