如何做依赖注入python-way?

Kon*_*rin 12 python design-patterns dependency-injection

我最近一直在阅读很多关于python-way的内容,所以我的问题是

如何做依赖注入python-way?

我正在谈论通常的情况,例如,服务A需要访问UserService以进行授权检查.

Tho*_*ers 14

这一切都取决于具体情况.例如,如果您使用依赖注入进行测试 - 因此您可以轻松地模拟某些东西 - 您通常可以完全放弃注入:您可以模拟出您注入的模块或类:

subprocess.Popen = some_mock_Popen
result = subprocess.call(...)
assert some_mock_popen.result == result
Run Code Online (Sandbox Code Playgroud)

subprocess.call()将调用subprocess.Popen(),我们可以模拟它,而不必以特殊的方式注入依赖.我们可以直接替换subprocess.Popen.(这只是一个例子;在现实生活中,你会以更加健壮的方式做到这一点.)

如果对更复杂的情况使用依赖注入,或者模拟整个模块或类是不合适的(例如,因为你只想模拟一个特定的调用)那么通常使用类属性或模块全局变量用于依赖选择.例如,考虑一个my_subprocess.py:

from subprocess import Popen

def my_call(...):
    return Popen(...).communicate()
Run Code Online (Sandbox Code Playgroud)

您可以轻松替换分配给的Popen呼叫; 它不会影响任何其他调用(当然它会替换所有调用.)同样,类属性:my_call()my_subprocess.Popensubprocess.Popenmy_subprocess.Popen

class MyClass(object):
    Popen = staticmethod(subprocess.Popen)
    def call(self):
        return self.Popen(...).communicate(...)
Run Code Online (Sandbox Code Playgroud)

当使用这样的类属性时,考虑到选项很少需要,你应该小心使用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(...)
Run Code Online (Sandbox Code Playgroud)

在设置类似的实例属性时,您永远不必担心触发描述符,因此只需分配函数(或类或其他callables或实例)就可以了.

  • 是的,如果你想编写高效的pythonic代码,你应该意识到Python是一种完全不同的语言.你做不同的事情.用于测试的Monkeypatching模块非常常见并且非常安全(特别是在使用适当的模拟库来处理细节时.)Monkeypatching类(更改类上的特定方法)是另一回事.None默认值实际上不是你应该关注的东西 - 这个例子实际上是通过构造函数非常接近DI,你可以省略默认值并使它成为必需参数. (2认同)