python 3的依赖注入库

serum的Python项目详细描述


生成状态

生成状态:

circleci

代码质量:

test coverage

maintability

说明

<代码>血清< /代码>是Python 3中一个新的依赖依赖注入。

<代码>血清< /代码>为纯Python,无依赖关系。

安装

> pip install serum

快速启动

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything

文档

注入

inject用于修饰要在其中注入的函数和类 依赖关系。

fromserumimportinject,dependency@dependencyclassMyDependency:pass@injectdeff(dependency:MyDependency):assertisinstance(dependency,MyDependency)f()

inject修饰的函数可以作为普通函数调用。<代码>血清< /代码> 不尝试在调用时插入给定的参数。

@injectdeff(dependency:MyDependency):print(dependency)f('Overridden dependency')#  outputs: Overridden dependency 

inject将实例化用依赖项修饰的类。在 这样,就可以使用inject依赖关系

简单类型和对象的实例可以是 使用关键字参数注入到上下文

@injectdeff(dependency:str):assertdependency=='a named dependency'withContext(dependency='a named dependency'):f()

inject也可用于修饰类。

@injectclassSomeClass:dependency:MyDependency

这大致相当于:

classSomeClass:@injectdef__init__(self,dependency:MyDependency):self.__dependency=dependency@propertydefdependency(self)->MyDependency:returnself.__dependency

可以重写指定为类级批注的依赖项 使用关键字参数来初始化

assertSomeClass(dependency='Overridden!').dependency=='Overridden!'

依赖关系

依赖关系修饰的类可以被实例化和注入 按<代码>血清< /代码>

fromserumimportdependency,inject@dependencyclassLog:definfo(self,message):print(message)@injectclassNeedsLog:log:Logassertisinstance(NeedsLog().log,Log)

<代码>血清< /代码>依赖于能够为<代码>依赖项装饰类注入所有依赖项 递归地。为了实现这一点,<代码>血清< /代码>假定<代码> 可以在不带任何参数的情况下调用ofdependency修饰类。 这意味着必须使用inject注入依赖项的\uu init的所有参数

> pip install serum
0

注意,循环依赖关系阻止了依赖关系的实例化 类导致错误。

> pip install serum
1

上下文

上下文提供依赖项的实现。a上下文 请求类型的特定子类型(按方法解析顺序)。

> pip install serum
2

上下文中插入一个类型是错误的,该上下文提供该类型的两个或多个同等特定的子类型:

> pip install serum
3

上下文也可以用作装饰口服补液盐:< >

> pip install serum
4

您可以使用关键字参数提供任何类型的命名依赖项。

> pip install serum
5

上下文对每个线程都是本地的。这意味着当使用多线程时 每个线程都在自己的上下文中运行

> pip install serum
6

singleton

若要始终在同一上下文中插入依赖项的同一实例,请使用singleton注释类型。

> pip install serum
7

注意singleton在不同环境中注入的依赖项 不会引用同一实例。

> pip install serum
8

模拟

<代码>血清<代码>支持从构建中注入<代码> MAICICMOK < /代码> s unittest.mock使用mock实用程序的unittests中的库 功能。模拟被重置 当环境上下文关闭时。

> pip install serum
9

mock使用其参数指定注入的magicmock实例。这意味着 试图调用未由模拟组件定义的方法 导致错误

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
0

注意,mock将只模拟 作为其参数提供的确切类型,但不是 或多或少的特定类型

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
1

匹配

match是用于匹配上下文的小型实用函数 使用环境变量的值。

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
2
fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
3
fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
4
fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
5

IPython集成

导入一些上下文并将其作为 每个ipython会话开始时的上下文管理器。 此外,您经常希望在特殊的上下文中运行ipython repl, 例如,提供通常通过命令行提供的配置 以其他方式争论。

<> P< >代码>血清< /代码>可作为IPython扩展。要激活它, 将以下行添加到ipython_config.py中:

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
6

最后,在项目的根目录中创建一个名为ipython_context.py的文件。在里面, 将要自动启动的上下文实例分配给全局 名为context的变量

fromserumimportinject,dependency,Context# Classes decorated with 'dependency' are injectable types.@dependencyclassLog:definfo(self,message:str):raiseNotImplementedError()classSimpleLog(Log):definfo(self,message:str):print(message)classStubLog(SimpleLog):definfo(self,message:str):pass@inject# Dependencies are injected using a class decorator...classNeedsLog:log:Log# ...and class level annotations...classNeedsSimpleLog:@inject# ...or using a function decoratordef__init__(self,log:SimpleLog):self.log=log@injectclassNeedsNamedDependency:named_dependency:str# class level annotations annotated with a type that is not# decorated with 'dependency' will be treated as a named# dependency# Contexts provide dependencieswithContext(SimpleLog,named_dependency='this name is injected!'):assertisinstance(NeedsLog().log,SimpleLog)assertNeedsNamedDependency().named_dependency=='this name is injected!'# Contexts will always provide the most specific # subtype of the requested type. This allows you to change which# dependencies are injected.withContext(StubLog):NeedsLog().log.info('Hello serum!')# doesn't output anythingNeedsSimpleLog().log.info('Hello serum!')# doesn't output anything
7

ipython现在将在 每个repl会话都在项目的根目录中启动。

为什么?

如果你一直在研究python的依赖注入框架, 毫无疑问,您会遇到这样的意见:

< Buff行情>

在python中不需要依赖注入。 你可以用鸭子打字和猴子修补!

这句话背后的立场通常是你只需要依赖 以静态类型语言注入。

实际上,在任何语言中都不需要依赖注入, 静态类型的或其他类型的。 然而,当构建需要在多个环境中运行的大型应用程序时, 依赖注入可以让你的生活更轻松。以我的经验, 过度使用猴子补丁来管理环境会导致混乱 隐式初始化步骤混乱,如果值为none,则输入代码。

除了作为一个框架外,我还尝试设计<代码>血清<代码> 设计遵循依赖倒置原则的类:

< Buff行情>

一个人应该"依赖抽象,而不是具体化。"

这是通过让继承成为提供 依赖关系并允许依赖关系是抽象的。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
我无法从java运行python脚本,我认为这是因为该脚本没有执行权限   Java Http客户端通过POST上传文件   java检查一个枚举对象是否顺序在另一个枚举对象之后?   java MOXy JAXB在XmlIDREF上断开为具有enum属性的类型   java如何在安卓应用程序中集成大地水准面模型计算?   使用Jackson和Hibernate的java多对多无限递归   Java显然是一个传递值,但需要一些澄清   Android Studio Room中带参数的java查询   java我想根据表单中输入的数字更改屏幕转换目的地   java正则表达式:所有格量词中的回溯   java当元素的数量导致在回收器上生成滚动时,如何将一个recyclerView的元素与另一个元素分开?   如何将结果集作为输入从java传递到oracle存储过程   java问答应用程序在最后一个问题中未显示对话框   令牌“{”上的java语法错误,应在该令牌之后切换标签   java我的while循环有什么问题?   java Spring 5 webclient调用没有超时   ArrayList中的java对象不使用泛型保留其类型