【问题标题】:Replicating Guide @Named with Python injector module使用 Python 注入器模块复制指南 @Named
【发布时间】:2021-01-05 17:49:13
【问题描述】:

我正在使用 injector 模块,它体现了用于 DI 框架的大部分 Guice API。

我试图通过 @Named 绑定注释完成 Guice 所做的事情。也就是说,使用injector.Module覆盖configure方法,使用Binder绑定多个相同类型但名称不同的实例:

import pathlib
import injector

ConfigFile = pathlib.WindowsPath
UserFile = pathlib.WindowsPath

cfg_file = pathlib.Path.home() / 'settings.cfg'
usr_file = pathlib.Path.home() / 'user.inf'

class AppModule(injector.Module):

    def configure(self, binder):
        binder.bind(ConfigFile, to=cfg_file) # Guice uses annotatedWith here
        binder.bind(UserFile, to=usr_file) # Guice uses annotatedWith here

class App:

    @injector.inject
    def __init__(self, cfg: ConfigFile, usr: UserFile):
        self.config = cfg
        self.user = usr

app = injector.Injector(AppModule).get(App)
print(app.config, app.user) # prints the contents of `usr_file` for both

我认为Binder.multibind 我有一些生存能力,但我的尝试没有成功。我可以设计的唯一解决方案是必须将pathlib.Path 子类化,并且这些子类显式绑定到它们的实例(可以在injector.Module 中使用@provider@singleton 一起管理)。

是否有另一种无法解决子类的方法来完成此任务?

【问题讨论】:

    标签: python dependency-injection guice


    【解决方案1】:

    可以使用来自 typing_extensions 的 Annotated(已经是注入器的依赖项或来自 Python 3.9 的类型)。在上面的示例中,只需替换

    ConfigFile = pathlib.WindowsPath
    UserFile = pathlib.WindowsPath
    

    ConfigFile = Annotated[pathlib.WindowsPath, 'config']
    UserFile = Annotated[pathlib.WindowsPath, 'user']
    

    它应该可以工作。

    使用 typing.NewType 都是可行的,但它可能无法很好地与静态类型检查器配合使用。

    这是在 python 3.6 上测试的完整工作示例:

    from injector import Injector, inject, Module, singleton, provider
    from typing_extensions import Annotated
    
    from typing import NewType, Callable
    
    # You can use Greeting instead of Annotated[str, 'greeting'] as it's the same type
    Greeting = Annotated[str, 'greeting']
    
    
    class Greeter:
        @inject
        def __init__(self, greeting: Annotated[str, 'greeting']):
            self.greeting = greeting
    
        def greet(self):
            print(self.greeting)
    
    
    class MyModule(Module):
    
        def configure(self, binder):
            binder.bind(Greeting, to='Howdy', scope=singleton)
            binder.bind(str, to='Hello', scope=singleton)
    
    
    def main():
        injector = Injector([MyModule()])
        greeter = injector.get(Greeter)
        greeter.greet()
    
    
    if __name__ == '__main__':
        main()
    

    有一点问题,如果你不提供Annotated[str, 'greeting'] 的绑定,注入器将使用一个默认构造函数,它会产生一些东西,但可能不是你所期望的。另一方面,它对 str 的处理方式相同——愉快地构造了一个空字符串。

    在安装了以下软件包的 conda 环境中进行测试(使用 pip 安装注入器):

    # Name                    Version
    injector                  0.18.4 
    libffi                    3.0.9  
    pip                       18.1   
    python                    3.6.8  
    setuptools                40.8.0 
    sqlite                    3.26.0 
    typing-extensions         3.7.4.3
    wheel                     0.33.1 
    xz                        5.2.3  
    

    【讨论】:

    • 我没有意识到这一点,似乎 PEP-593 将其添加到 Python 3.9,不幸的是我使用的是 Python 3.7,但 injector 模块可以通过 typing_extensions 访问 injector.Annotated .事实上,我使用typing.NewType 来执行此操作,但正如您所猜测的,静态类型检查器会失败。
    • 它在 Python 3.6 中对我来说很好,至少对于接线本身来说。我从typing_extensions 导入注释。不过,我没有使用任何静态类型检查器对其进行测试。
    • 你能在这里展示一个完整的例子来说明你如何使用Annotated吗?我无法让它工作,Binder.bind 抛出 injector.UnknownProvider
    • 我已经更新了帖子,因为示例太长,无法作为评论发布。
    • 您使用的是哪个版本的injector?在 Python 3.6、3.7 和 3.9 上测试了您的更新,都返回了我收到的相同错误。
    猜你喜欢
    • 1970-01-01
    • 2011-05-06
    • 2018-12-23
    • 2016-11-25
    • 2011-10-13
    • 2011-02-23
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    相关资源
    最近更新 更多