No Java IoC / DI, é uma prática muito comum usada extensivamente em aplicativos da Web, quase todas as estruturas disponíveis e Java EE. Por outro lado, também existem muitas aplicações Web em Python, mas além do Zope (que ouvi dizer que deve ser realmente horrível de codificar), a IoC não parece ser muito comum no mundo Python. (Por favor, cite alguns exemplos, se você acha que estou errado).
É claro que existem vários clones de estruturas populares de Java IoC disponíveis para Python, springpython , por exemplo. Mas nenhum deles parece se acostumar praticamente. Pelo menos, nunca me deparei com um aplicativo da Web baseado em Django ou sqlalchemy + <insert your favorite wsgi toolkit here>
que usa algo parecido.
Na minha opinião, a IoC tem vantagens razoáveis e facilitaria a substituição do modelo django-default-user-model, por exemplo, mas o uso extensivo de classes de interface e IoC no Python parece um pouco estranho e não »pythonic«. Mas talvez alguém tenha uma explicação melhor, por que a IoC não é amplamente usada em Python.
Respostas:
Na verdade, não acho que DI / IoC seja tão incomum em Python. O que é incomum, no entanto, são estruturas / contêineres DI / IoC .
Pense nisso: o que um contêiner DI faz? Permite que você
Temos nomes para "ligar juntos" e "em tempo de execução":
Portanto, um contêiner de DI não é senão um intérprete para uma linguagem de script dinâmico. Na verdade, deixe-me reformular: um contêiner DI Java / .NET típico nada mais é do que um intérprete de baixa qualidade para uma linguagem de script dinâmica realmente ruim com sintaxe horrível, às vezes baseada em XML.
Quando você programa em Python, por que você gostaria de usar uma linguagem de script feia e ruim quando tem uma linguagem de script bonita e brilhante à sua disposição? Na verdade, essa é uma pergunta mais geral: quando você programa em praticamente qualquer linguagem, por que deseja usar uma linguagem de script feia e ruim quando tem o Jython e o IronPython à sua disposição?
Então, para recapitular: a prática de DI / IoC é tão importante em Python quanto em Java, exatamente pelas mesmas razões. A implementação do DI / IoC, no entanto, é incorporada à linguagem e geralmente é tão leve que desaparece completamente.
(Aqui está um breve resumo de uma analogia: na montagem, uma chamada de sub-rotina é muito importante - você precisa salvar as variáveis locais e os registros na memória, salvar o endereço de retorno em algum lugar, alterar o ponteiro da instrução para a sub-rotina que você está chamando, organize para que, de alguma forma, volte à sua sub-rotina quando terminar, coloque os argumentos em algum lugar onde o receptor possa encontrá-los, etc. IOW: em assembly, "chamada de subrotina" é um padrão de design e antes havia idiomas como Para o Fortran que tinha chamadas de sub-rotina integradas, as pessoas estavam criando suas próprias "estruturas de sub-rotina". Você diria que as chamadas de sub-rotina são "incomuns" no Python, apenas porque você não usa estruturas de sub-rotina?)
BTW: para um exemplo de como se parece a tomar DI à sua conclusão lógica, dê uma olhada Gilad Bracha 's Novilíngua Linguagem de programação e seus escritos sobre o assunto:
fonte
Parte disso é a maneira como o sistema de módulos funciona em Python. Você pode obter uma espécie de "singleton" de graça, apenas importando-o de um módulo. Defina uma instância real de um objeto em um módulo e, em seguida, qualquer código de cliente pode importá-lo e obter um objeto funcional, totalmente construído / preenchido.
Isso contrasta com o Java, onde você não importa instâncias reais de objetos. Isso significa que você sempre precisará instancia-los você mesmo (ou usar algum tipo de abordagem no estilo IoC / DI). Você pode atenuar o incômodo de ter que instanciar tudo sozinho usando métodos estáticos de fábrica (ou classes reais de fábrica), mas ainda incorre na sobrecarga de recursos para criar novos a cada vez.
fonte
MyClassInstances
classe para cada umaMyClass
em Java, que contém apenas instâncias estáticas e totalmente inicializadas. Isso seria conectado: Dfrom framework.auth.user import User
, seria melhor escreverUser = lookup('UserImplentation', 'framework.auth.user.User')
(o segundo parâmetro pode ser um valor padrão) dentro da estrutura. Em seguida, os usuários da estrutura poderão substituir / especializar aUser
implementação sem tocar na estrutura.IoC e DI são super comuns em código Python maduro. Você simplesmente não precisa de uma estrutura para implementar o DI, graças à digitação do duck.
O melhor exemplo é como você configura um aplicativo Django usando
settings.py
:O Django Rest Framework utiliza DI intensamente:
Deixe-me lembrar ( fonte ):
fonte
IsAuthenticated
,ScopedRateThrottle
) são instanciadas pela classe. Eles não são passados para o construtor.IsAuthenticated
eScopedRateThrottle
não são instâncias, são classes. Eles são instanciados quando um FooView é construído (na verdade, quando o FooView processa uma solicitação). Enfim, isso é apenas um detalhe de implementação.IsAuthenticated
eScopedRateThrottle
são as dependências; eles são injetados noFooView
. Não importa quando ou como isso é feito. Python não é Java, portanto, existem diferentes maneiras de implementar isso.renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer)
. Zombar é tão simples quanto@unittest.patch('myapp.views.FooView.permission_classes')
. Uma necessidade desesperada de "passar alguma coisa" é uma conseqüência da "maneira de fazer as coisas do Java", devido ao fato de o Java ser uma linguagem compilada e com estaticamente tipicamente sem grandes recursos de metaprogramação.O Django faz um ótimo uso da inversão de controle. Por exemplo, o servidor de banco de dados é selecionado pelo arquivo de configuração e, em seguida, a estrutura fornece instâncias apropriadas de wrapper do banco de dados para os clientes do banco de dados.
A diferença é que o Python tem tipos de primeira classe. Os tipos de dados, incluindo classes, são objetos. Se você quiser que algo use uma classe específica, simplesmente nomeie a classe. Por exemplo:
O código posterior pode criar uma interface de banco de dados escrevendo:
Em vez das funções de fábrica padrão que Java e C ++ precisam, o Python faz isso com uma ou duas linhas de código comum. Essa é a força da programação funcional versus imperativa.
fonte
import psycopg2 as database_interface
. Coloque essa linha em uminjections.py
et voilà.self.database_interface
), que gritam imperativos.Parece que as pessoas realmente não entendem mais o que significa injeção de dependência e inversão de controle.
A prática de usar inversão de controle é ter classes ou funções que dependam de outras classes ou funções, mas, em vez de criar instâncias na classe de código de função, é melhor recebê-lo como parâmetro, para que o acoplamento solto possa ser arquivado. Isso tem muitos benefícios como mais testabilidade e arquivar o princípio de substituição liskov.
Veja que, trabalhando com interfaces e injeções, seu código fica mais fácil de manutenção, pois você pode alterar o comportamento facilmente, porque não precisará reescrever uma única linha de código (talvez uma ou duas linhas na configuração DI) de seu para alterar seu comportamento, pois as classes que implementam a interface que sua classe está esperando podem variar de forma independente, desde que sigam a interface. Uma das melhores estratégias para manter o código dissociado e fácil de manter é seguir pelo menos os princípios de responsabilidade única, substituição e inversão de dependência.
Para que serve uma biblioteca de DI se você pode instanciar um objeto dentro de um pacote e importá-lo para injetar você mesmo? A resposta escolhida é correta, uma vez que o java não possui seções processuais (código fora das classes), tudo o que entra em xmls da configuração chata, daí a necessidade de uma classe instanciar e injetar dependências em um estilo de carregamento lento para que você não se estrague seu desempenho, enquanto em python você apenas codifica as injeções nas seções "procedurais" (código fora das classes) do seu código
fonte
Não uso o Python há vários anos, mas eu diria que ele tem mais a ver com ser uma linguagem de tipo dinâmico do que qualquer outra coisa. Para um exemplo simples, em Java, se eu quisesse testar se alguma coisa foi escrita corretamente, eu poderia usar o DI e passar qualquer PrintStream para capturar o texto que está sendo escrito e verificá-lo. No entanto, quando estou trabalhando no Ruby, posso substituir dinamicamente o método 'puts' no STDOUT para verificar, deixando o DI completamente fora de cena. Se o único motivo pelo qual estou criando uma abstração é testar a classe que está sendo usada (pense nas operações do sistema de arquivos ou no relógio em Java), o DI / IoC cria complexidade desnecessária na solução.
fonte
Na verdade, é muito fácil escrever código suficientemente limpo e compacto com DI (eu me pergunto, será / permanecerá) pitonico , mas de qualquer maneira :)), por exemplo, eu realmente percebo essa maneira de codificar:
_
Sim, isso pode ser visto apenas como uma forma simples de parametrizar funções / classes, mas funciona. Portanto, talvez as baterias incluídas no padrão do Python também sejam suficientes aqui.
PS: Também publiquei um exemplo maior dessa abordagem ingênua na avaliação dinâmica da lógica booleana simples em Python .
fonte
IoC / DI é um conceito de design, mas, infelizmente, geralmente é tomado como um conceito que se aplica a certos idiomas (ou sistemas de digitação). Eu adoraria ver os contêineres de injeção de dependência se tornarem muito mais populares no Python. Existe o Spring, mas essa é uma superestrutura e parece ser uma porta direta dos conceitos Java sem muita consideração pelo "The Python Way".
Dadas as anotações no Python 3, decidi fazer um crack em um contêiner de injeção de dependência com todos os recursos, mas simples: https://github.com/zsims/dic . Ele é baseado em alguns conceitos de um contêiner de injeção de dependência do .NET (que IMO é fantástico se você estiver jogando nesse espaço), mas sofreu alterações nos conceitos do Python.
fonte
Acho que devido à natureza dinâmica das pessoas em python, muitas vezes não vêem a necessidade de outra estrutura dinâmica. Quando uma classe é herdada do 'objeto' de novo estilo, você pode criar uma nova variável dinamicamente ( https://wiki.python.org/moin/NewClassVsClassicClass ).
ie, em python simples:
No entanto, dê uma olhada em https://github.com/noodleflake/pyioc. Isso pode ser o que você está procurando.
ie em pyioc
fonte
other.py
linha 1, há uma resolução de dependência automatizada, mas não contaria isso como uma injeção de dependência.Apoio a resposta "Jörg W Mittag": "A implementação do DI / IoC em Python é tão leve que desaparece completamente".
Para fazer backup dessa declaração, dê uma olhada no famoso exemplo de Martin Fowler portado de Java para Python: Python: Design_Patterns: Inversion_of_Control
Como você pode ver no link acima, um "Contêiner" em Python pode ser escrito em 8 linhas de código:
fonte
Meus 2 centavos é que, na maioria dos aplicativos Python, você não precisa e, mesmo que precise, é provável que muitos haters Java (e violadores incompetentes que acreditam ser desenvolvedores) considerem isso algo ruim, apenas porque é popular em Java .
Um sistema IoC é realmente útil quando você possui redes complexas de objetos, onde cada objeto pode ser uma dependência de vários outros e, por sua vez, ser dependente de outros objetos. Nesse caso, convém definir todos esses objetos uma vez e ter um mecanismo para reuni-los automaticamente, com base no maior número possível de regras implícitas. Se você também possui uma configuração a ser definida de maneira simples pelo usuário / administrador do aplicativo, esse é um motivo adicional para desejar um sistema IoC que possa ler seus componentes a partir de algo como um arquivo XML simples (que seria a configuração).
O aplicativo típico do Python é muito mais simples, apenas um monte de scripts, sem uma arquitetura tão complexa. Pessoalmente, estou ciente do que realmente é uma IoC (ao contrário dos que escreveram certas respostas aqui) e nunca senti a necessidade disso em minha experiência limitada em Python (também não uso o Spring em todos os lugares, não quando as vantagens não justifica a sobrecarga de desenvolvimento).
Dito isto, há situações em Python em que a abordagem de IoC é realmente útil e, de fato, li aqui que o Django a usa.
O mesmo raciocínio acima pode ser aplicado à Programação Orientada a Aspectos no mundo Java, com a diferença de que o número de casos em que a AOP realmente vale a pena é ainda mais limitado.
fonte
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- uma suposiçãoacessórios pytest todos baseados em DI ( fonte )
fonte
Eu concordo com o @Jorg no ponto em que o DI / IoC é possível, mais fácil e ainda mais bonito em Python. O que falta são as estruturas que o suportam, mas existem algumas exceções. Para apontar alguns exemplos que me vêm à mente:
Os comentários do Django permitem que você conecte sua própria classe Comment com sua lógica e formulários personalizados. [Mais informações]
O Django permite que você use um objeto Profile personalizado para anexar ao seu modelo de usuário. Isso não é completamente IoC, mas é uma boa abordagem. Pessoalmente, eu gostaria de substituir o modelo de usuário do furo como a estrutura de comentários. [Mais informações]
fonte
Na minha opinião, coisas como injeção de dependência são sintomas de uma estrutura rígida e excessivamente complexa. Quando o corpo principal do código se torna pesado demais para mudar com facilidade, você precisa escolher pequenas partes dele, definir interfaces para eles e permitir que as pessoas alterem o comportamento por meio dos objetos que se conectam a essas interfaces. Tudo bem, mas é melhor evitar esse tipo de complexidade em primeiro lugar.
É também o sintoma de uma linguagem de tipo estaticamente. Quando a única ferramenta que você precisa para expressar a abstração é a herança, é isso que você usa em qualquer lugar. Dito isto, o C ++ é bastante semelhante, mas nunca captou o fascínio por Builders e Interfaces em todos os lugares que os desenvolvedores Java fizeram. É fácil ficar exuberante com o sonho de ser flexível e extensível ao custo de escrever muito código genérico com pouco benefício real . Eu acho que é uma coisa cultural.
Normalmente, acho que as pessoas Python estão acostumadas a escolher a ferramenta certa para o trabalho, que é um todo coerente e simples, em vez da Única Ferramenta Verdadeira (com Mil Plugins Possíveis) que pode fazer qualquer coisa, mas oferece uma variedade desconcertante de possíveis permutações de configuração . Ainda existem partes intercambiáveis, quando necessário, mas sem a necessidade do grande formalismo da definição de interfaces fixas, devido à flexibilidade da digitação de patos e à relativa simplicidade da linguagem.
fonte
EmailSender
e decidir substituí-la por umaDesktopNotifier
, tenho que editar 12 classes manualmente. E você acha que é mais simples e limpo que basta escrever em umaINotifier
interface e deixar o contêiner trabalhar com os detalhes?Ao contrário da forte natureza tipada em Java. O comportamento de digitação de pato do Python facilita a passagem de objetos.
Os desenvolvedores de Java estão focados na construção da estrutura de classe e na relação entre objetos, mantendo as coisas flexíveis. A IoC é extremamente importante para conseguir isso.
Os desenvolvedores de Python estão focados em fazer o trabalho. Eles apenas ligam as aulas quando precisam. Eles nem precisam se preocupar com o tipo de classe. Contanto que possa grasnar, é um pato! Essa natureza não deixa espaço para a IoC.
fonte