Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何以python的方式进行依赖项注入?_Python_Design Patterns_Dependency Injection - Fatal编程技术网

如何以python的方式进行依赖项注入?

如何以python的方式进行依赖项注入?,python,design-patterns,dependency-injection,Python,Design Patterns,Dependency Injection,最近我读了很多关于pythonway的书,所以我的问题是 如何以python的方式进行依赖项注入 我说的是通常的场景,例如,服务A需要访问UserService进行授权检查。这取决于具体情况。例如,如果您使用依赖项注入进行测试——这样您就可以很容易地模拟某些东西——您通常可以完全放弃注入:您可以模拟原本要注入的模块或类: subprocess.Popen = some_mock_Popen result = subprocess.call(...) assert some_mock_popen.

最近我读了很多关于pythonway的书,所以我的问题是

如何以python的方式进行依赖项注入


我说的是通常的场景,例如,服务A需要访问UserService进行授权检查。

这取决于具体情况。例如,如果您使用依赖项注入进行测试——这样您就可以很容易地模拟某些东西——您通常可以完全放弃注入:您可以模拟原本要注入的模块或类:

subprocess.Popen = some_mock_Popen
result = subprocess.call(...)
assert some_mock_popen.result == result
subprocess.call()
将调用
subprocess.Popen()
,我们可以模拟它,而无需以特殊方式注入依赖项。我们可以直接替换
subprocess.Popen
。(这只是一个例子;在现实生活中,你会以一种更加稳健的方式来实现这一点。)

如果在更复杂的情况下使用依赖项注入,或者当模拟整个模块或类不合适时(例如,因为您只想模拟一个特定调用),则通常会选择使用类属性或模块全局变量来模拟依赖项。例如,考虑一个
my_subprocess.py

from subprocess import Popen

def my_call(...):
    return Popen(...).communicate()
通过分配给
my_subprocess.Popen
,您可以轻松地仅替换
my_call()
发出的
Popen
调用;它不会影响对
subprocess.Popen
的任何其他调用(当然,它会替换对
my_subprocess.Popen
的所有调用)。类似地,类属性:

class MyClass(object):
    Popen = staticmethod(subprocess.Popen)
    def call(self):
        return self.Popen(...).communicate(...)
当使用这样的类属性时(考虑到选项很少需要),应该注意使用
staticmethod
。如果不这样做,并且插入的对象是普通函数对象或其他类型的描述符(如属性),在从类或实例检索时执行特殊操作,那么它将执行错误的操作。更糟糕的是,如果您使用的对象现在不是描述符(如本例中的
subprocess.Popen
类),那么它现在就可以工作,但是如果所讨论的对象将来更改为正常函数,它将混乱地中断

最后,只有简单的回调;如果您只想将一个类的特定实例绑定到一个特定的服务,您可以将该服务(或该服务的一个或多个方法)传递给类初始值设定项,并让它使用:

class MyClass(object):
    def __init__(self, authenticate=None, authorize=None):
        if authenticate is None:
            authenticate = default_authenticate
        if authorize is None:
            authorize = default_authorize
        self.authenticate = authenticate
        self.authorize = authorize
    def request(self, user, password, action):
        self.authenticate(user, password)
        self.authorize(user, action)
        self._do_request(action)

...
helper = AuthService(...)
# Pass bound methods to helper.authenticate and helper.authorize to MyClass.
inst = MyClass(authenticate=helper.authenticate, authorize=helper.authorize)
inst.request(...)
当设置这样的实例属性时,您永远不必担心描述符触发,因此只需分配函数(或类或其他可调用项或实例)就可以了。

这个“仅setter”注入配方怎么样?


它非常类似于Python,使用带有
\uuuuu get\uuuuuuuuuuuuuuuuuuuuuuuu()
/
\uuuuuuuuu set\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,需要将所有属性设置代码替换为
RequiredFeature
实例,该实例使用
Feature
required的str名称初始化。

我最近发布了一个用于python的DI框架,这可能对您有所帮助。我认为这是一个相当新鲜的想法,但我不确定它有多“pythonic”。你自己判断吧。欢迎反馈


在多年使用Python而不使用任何DI自动连接框架和Java with Spring之后,我逐渐意识到简单的Python代码通常不需要框架来进行依赖注入而不使用自动连接(Guice和Spring在Java中都是自动连接的),也就是说,这样做就足够了:

def-foo(dep=None):#非常适合单元测试!
self.dep=dep或dep()#呼叫者也不能关心这一点
...
这是纯粹的依赖注入(非常简单),但没有神奇的框架为您自动注入它们(即自动连线),也没有控制反转

尽管在我处理更大的应用程序时,这种方法不再适用。因此,我提出了一个微型框架,它不会让人觉得不符合python,但可以提供一流的依赖注入自动连接

在人类的座右铭下™ 这就是它看起来的样子:

#some_service.py
服务类别:
@自动连线
定义初始化__(
自己
数据库:自动连线(数据库),
消息代理:自动连线(列表[代理]),
):
挂起=数据库。检索挂起的消息()
对于message_brokers中的broker:
broker.send_挂起(挂起)
#database.py
@注射的
类数据库:
...
#message_broker.py
类别MessageBroker(ABC):
def发送_挂起(消息):
...
#kafka#u producer.py
@注射的
KafkaProducer类(MessageBroker):
...
#sqs_producer.py
@注射的
SQSProducer类(MessageBroker):
...
什么是依赖项注入? 依赖注入是一种有助于减少耦合和增加内聚的原则

耦合和内聚关系到组件的连接强度

  • 高耦合。如果联轴器很高,就像使用强力胶或焊接一样。不容易拆卸
  • 高内聚力。高内聚性就像使用螺丝钉一样。非常容易拆卸和组装回或以不同的方式组装。它与高耦合相反
当内聚力较高时,耦合度较低

低耦合带来了灵活性。您的代码变得更易于更改和测试

如何实现依赖注入? 对象不再相互创建。它们提供了一种注入依赖项的方法

之前:


导入操作系统
类ApiClient:
定义初始化(自):
self.api_key=os.getenv('api_key')#我有C#背景,所以我习惯于通过构造函数显式注入,并使用容器解决所有问题。在初始值设定项中指定默认值为None看起来像“穷人的DI”,这在我看来不是最佳实践,因为它不够明确。关于更换exi的类似想法