惰性可调用函数的Python依赖注入

惰性可调用函数的Python依赖注入,python,dependency-injection,lazy-evaluation,Python,Dependency Injection,Lazy Evaluation,在有趣的编程中,我注意到管理依赖关系感觉像是一件无聊的杂事,我想将其最小化,我提出了一个非常简单的依赖项注入器,通过字符串键查找依赖项实例: def run_job(job, args, instance_keys, injected): args.extend([injected[key] for key in instance_keys]) return job(*args) 这种廉价的技巧之所以有效,是因为我的程序中的调用总是在迭代器中惰性地定义(函数句柄与其参数分开存储

在有趣的编程中,我注意到管理依赖关系感觉像是一件无聊的杂事,我想将其最小化,我提出了一个非常简单的依赖项注入器,通过字符串键查找依赖项实例:

def run_job(job, args, instance_keys, injected):
    args.extend([injected[key] for key in instance_keys])
    return job(*args)
这种廉价的技巧之所以有效,是因为我的程序中的调用总是在迭代器中惰性地定义(函数句柄与其参数分开存储),例如:

jobs_to_run = [[some_func, ("arg1", "arg2"), ("obj_key",)], [other_func,(),()]]
原因是因为中央
主循环必须安排所有事件。它引用了所有依赖项,因此可以在dict对象中传递
“obj_key”
的注入,例如:

# inside main loop
injection = {"obj_key" : injected_instance}
for (callable, with_args, and_dependencies) in jobs_to_run: 
    run_job(callable, with_args, and_dependencies, injection)
因此,当事件发生(用户输入等)时,主循环可能会调用对该输入作出反应的特定对象上的
update()
,该对象反过来为
main循环
建立作业列表,以便在有资源时进行调度。对我来说,键引用任何依赖项以便其他人注入比让所有对象形成直接关系更干净

因为我只是懒洋洋地为一个函数定义所有的可调用项(函数),所以上述简单的方法只增加了很少的复杂性。尽管如此,必须通过字符串引用对象还是有一种代码臭味。同时,到处传递依赖项是很糟糕的,而且可能会像大多数大型企业一样,被过度杀戮

对于在惰性定义的可调用项中注入依赖项的特殊情况,是否存在更具表现力的设计模式

我注意到,管理依赖关系感觉像是一件无聊的琐事,我想把它最小化

首先,您不应该假设依赖项注入是一种最小化依赖项管理琐事的方法。它不会消失,它只是被推迟到另一个地方和时间,并可能委托给其他人

这就是说,如果您正在构建的内容将被其他人使用,那么明智的做法是将某种形式的版本检查包含到您的“可注入内容”中,这样您的用户将有一种简单的方法来检查他们的版本是否与预期的版本匹配

是否存在更具表现力的设计模式

据我所知,您的方法本质上是一个,即作业的代码(可调用)依赖于对几个具体对象之一调用方法。你这样做是完全合理的-它是有效的

您可能希望将其形式化一点,使其更易于阅读和维护,例如

from collections import namedtuple

Job = namedtuple('Job', ['callable', 'args', 'strategies'])

def run_job(job, using=None):
    strategies = { k: using[k] for k in job.strategies] }
    return job.callable(*args, **strategies)

jobs_to_run = [
  Job(callable=some_func, args=(1,2), strategies=('A', 'B')),
  Job(callable=other_func, ...),
]

strategies = {"A": injected_strategy, ...}
for job in jobs_to_run: 
   run_job(job, using=strategies)

# actual job
def some_func(arg1, arg2, A=None, B=None):
   ...

正如您所看到的,代码仍然做同样的事情,但它立即更具可读性,并且它集中了关于
run\u Job
中Job()对象结构的知识。此外,如果传递的参数数量错误,对作业函数(如
some_func
)的调用也会失败,并且作业函数更容易编码和调试,因为它们明确列出并命名了参数。

关于字符串,您可以在
dependencies.py
文件中使用这些常量

一个开销仍然很小的更健壮的选项是使用依赖项注入框架,例如:

@autowired
def job42(some_实例:自动连线(“SomeInstance”,lazy=true)):
...
#某些_实例自动连接到job42调用和
#它会自动为你注射
job42()

披露:我是项目维护者。

您可能会改进您当前的方法,使用
kwargs
构造来命名函数参数,然后将其视为可注入的
dict
。这样,原始字符串作为函数参数更显式。