导入的python模块中的依赖注入

时间:2015-11-22 13:56:17

标签: python

我的python模块使用来自另一个模块的一些函数,但我有几个模块接口的实现。如何指出,使用哪一个?

简单示例:

A.py:

import B
def say_hi()
   print "Message: " + B.greeting()

main.py:

import A(B=my_B_impl)
A.say_hi()

my_B_impl.py:

def greeting():
   return "Hallo!"

输出:

Message: Hallo!

1 个答案:

答案 0 :(得分:0)

你问的不是直接可能的。 Python的模块系统没有内置参数化功能。如果您考虑一下,不清楚这样的提案应该如何工作:如果模块AB都导入模块M,但它们提供不同的参数,当使用哪个参数时导入M?是进口两次吗?这对于模块级配置意味着什么(如在logging中)?如果第三个模块C尝试导入M而没有参数,情况会变得更糟。此外,您可以从外部覆盖任何 import语句的“开放世界”理念违反了“您编写的代码就是运行的代码”的语言设计原则。 / p>

其他语言已经以各种方式合并了参数化模块(比较Scala的对象模型,ML的模块和签名,以及 - 拉伸它--C ++的模板),但是不清楚这样的特性是否适合Python。 (也就是说,如果你有足够的决心和自虐,你可能会使用importlib破解类似于参数化模块的东西。)

但是,Python确实具有非常强大和灵活的动态调度功能。 Python的标准日常功能,如函数,类,参数和覆盖,为这种支持提供了基础。

有很多方法可以削减你的行为可由其客户配置的函数示例。

由值:

参数化的函数
def say_hi(greeting):
    print("Message: " + greeting)

def main():
    say_hi("Hello")

由值:

参数化的类
class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting
    def say_hi(self):
        print("Message: " + self.greeting)


def main():
    Greeter("Hello").say_hi()

具有虚拟方法的类:

class Greeter:
    def say_hi(self):
        print("Message: " + self.msg())

class MyGreeter(Greeter):
    def msg(self):
        return "Hello"

由函数参数化的函数:

def say_hi(greeting):
    print("Message: " + greeting())

def make_greeting():
    return "Hello"

def main():
    say_hi(make_greeting)

还有更多选项(我正在避免使用Java-y对象调用其他对象的例子),但是你明白了。在每种情况下,行为的选择(参数的传递,方法的覆盖)与使用它的代码分离,并且可以放在不同的文件中。选择合适的人选取决于你的情况(虽然这里有一个提示:正确的一个总是最简单的一个)。

更新:在评论中,您提到您想要一个在模块级别设置依赖关系的API。这个问题的主要问题是依赖关系是 global - 模块是单例,因此导入模块的任何人都必须使用相同的依赖实现。

我的建议是提供一个面向对象的API,它具有“正确的”(每个实例)依赖注入,并提供顶级的便利功能,这些功能使用依赖性的(可配置的)“默认”设置。然后,您可以使用全局配置的版本选择 not 。这大约是how asyncio does it

# flexible object with dependency injection
class Greeter:
    def __init__(self, msg):
        self.msg = msg
    def say_hi(self):
        print("Message: " + self.msg)

# set up a default configuration of the object to be used by the high-level API
_default_greeter = Greeter("Hello")
def configure(msg):
    global _default_greeter
    _default_greeter = Greeter(msg)

# delegate to whatever default has been configured
def say_hi():
    _default_greeter.say_hi()