依赖注入的Pythonic方法是什么? 介绍
对于Java,依赖项注入与纯OOP一样工作,即提供要实现的接口,并在框架代码中接受实现已定义接口的类的实例 现在对于Python,您也可以用同样的方法,但是我认为对于Python来说,这种方法的开销太大了。那么,您将如何以Pythonic的方式实现它呢 用例 假设这是框架代码:依赖注入的Pythonic方法是什么? 介绍,python,oop,authentication,dependency-injection,frameworks,Python,Oop,Authentication,Dependency Injection,Frameworks,对于Java,依赖项注入与纯OOP一样工作,即提供要实现的接口,并在框架代码中接受实现已定义接口的类的实例 现在对于Python,您也可以用同样的方法,但是我认为对于Python来说,这种方法的开销太大了。那么,您将如何以Pythonic的方式实现它呢 用例 假设这是框架代码: class FrameworkClass(): def __init__(self, ...): ... def do_the_job(self, ...): # som
class FrameworkClass():
def __init__(self, ...):
...
def do_the_job(self, ...):
# some stuff
# depending on some external function
基本方法
最简单的(也许是最好的)方法是要求将外部函数提供给FrameworkClass
构造函数,然后从do\u作业
方法调用
框架代码:
客户端代码:
问题:
问题很简短。有没有更好的常用的肾盂方法?或者任何支持这种功能的库
最新情况:具体情况
假设我开发了一个microweb框架,它使用令牌处理身份验证。该框架需要一个函数来提供从令牌获得的一些ID
,并获得与该ID
对应的用户
显然,框架对用户或任何其他特定于应用程序的逻辑一无所知,因此客户端代码必须将用户getter功能注入框架中,以使身份验证工作正常。我们在项目中进行依赖项注入的方法是使用lib。看看这本书。我强烈建议将其用于DI。仅仅使用一个函数是没有意义的,但是当您必须管理多个数据源等时,它开始变得非常有意义 按照您的示例,它可能类似于:
# framework.py
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self):
# some stuff
self.func()
您的自定义功能:
# my_stuff.py
def my_func():
print('aww yiss')
要在应用程序中的某个位置创建一个引导文件,以跟踪所有已定义的依赖项:
# bootstrap.py
import inject
from .my_stuff import my_func
def configure_injection(binder):
binder.bind(FrameworkClass, FrameworkClass(my_func))
inject.configure(configure_injection)
然后您可以这样使用代码:
# some_module.py (has to be loaded with bootstrap.py already loaded somewhere in your app)
import inject
from .framework import FrameworkClass
framework_instance = inject.instance(FrameworkClass)
framework_instance.do_the_job()
我担心这是python所能做到的(这个模块有一些python的优点,比如通过参数注入decorators等等,请查看文档),因为python没有像接口或类型暗示这样的奇特功能
所以直接回答你的问题将非常困难。我认为真正的问题是:python对DI有一些本机支持吗?遗憾的是,答案是:没有。有关如何使用超级和多重继承而不是DI的争论,请参见。如果你没有时间看整个视频,跳到第15分钟(但我建议你全部看)
下面是一个如何将本视频中描述的内容应用于您的示例的示例:
框架代码:
客户端代码:
这将起作用,因为Python MRO将保证调用getUserFromToken客户机方法(如果使用super())。如果您使用的是Python2.x,则必须更改代码
这里的另一个好处是,如果客户端不提供实现,这将引发异常
当然,这不是真正的依赖注入,而是多重继承和混合,但它是一种解决问题的Pythonic方法。我认为DI和AOP通常不被认为是Pythonic的,因为典型的Python开发人员偏好,而不是语言特性
事实上,您可以在中实现一个基本的DI框架。不久前,我写了一个依赖注入微框架,其目标是使其成为Pythonic-。这就是您的代码在使用时的外观:
"""Example of dependency injection in Python."""
import logging
import sqlite3
import boto.s3.connection
import example.main
import example.services
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
logger = providers.Singleton(logging.Logger, name='example')
database = providers.Singleton(sqlite3.connect, ':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(example.services.UsersService,
logger=Platform.logger,
db=Platform.database)
auth = providers.Factory(example.services.AuthService,
logger=Platform.logger,
db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.PhotosService,
logger=Platform.logger,
db=Platform.database,
s3=Platform.s3)
class Application(containers.DeclarativeContainer):
"""IoC container of application component providers."""
main = providers.Callable(example.main.main,
users_service=Services.users,
auth_service=Services.auth,
photos_service=Services.photos)
下面是指向此示例更广泛描述的链接-
希望能对你有所帮助。有关更多信息,请访问:
- GitHub
- 文件
- 使用依赖项作为参数是一种非python方法。Python是一种OOP语言,具有漂亮而优雅的OOP模型,它提供了更直接的方法来维护依赖关系
- 为了模仿接口类型而定义充满抽象方法的类也很奇怪
- 巨大的包装上包装解决方案会产生代码开销
- 当我只需要一个小的模式时,我也不喜欢使用库
注意图案。我在一个真实的项目中使用了它,它显示出了一种不太好的方式 还有Pinject,Google开发的开源python依赖注入程序 这里有一个例子
>>> class OuterClass(object):
... def __init__(self, inner_class):
... self.inner_class = inner_class
...
>>> class InnerClass(object):
... def __init__(self):
... self.forty_two = 42
...
>>> obj_graph = pinject.new_object_graph()
>>> outer_class = obj_graph.provide(OuterClass)
>>> print outer_class.inner_class.forty_two
42
而且,一种非常简单的python式依赖注入方法importlib 您可以定义一个小的实用函数
def inject_method_from_module(modulename, methodname):
"""
injects dynamically a method in a module
"""
mod = importlib.import_module(modulename)
return getattr(mod, methodname, None)
然后你可以使用它:
myfunction = inject_method_from_module("mypackage.mymodule", "myfunction")
myfunction("a")
在mypackage/mymodule.py中定义myfunction
def myfunction(s):
print("myfunction in mypackage.mymodule called with parameter:", s)
当然,您也可以使用MyClass iso类。函数myfunction。如果在settings.py文件中定义methodname的值,则可以根据设置文件的值加载不同版本的methodname。Django正在使用这样一种模式来定义其数据库连接。依赖项注入是Python直接支持的一种简单技术。不需要额外的库。使用可提高清晰度和可读性 框架代码:
class UserStore():
"""
用于访问用户信息的基类。
客户端必须扩展此类并实现其方法。
"""
def get_名称(自身、令牌):
引发未实现的错误
类WebFramework():
定义初始化(self,用户存储:用户存储):
self.user\u store=用户\u store
def greet_用户(自身、令牌):
user\u name=self.user\u store.get\u name(令牌)
打印(f'Good day to you,{user_name}!')
客户端代码:
class AlwaysMaryUser(用户存储):
def get_名称(自身、令牌):
返回“玛丽”
类SQLUserStore(UserStore):
定义初始化(self,db_参数):
self.db_params=db_p
# Framework internal
def MetaIoC(name, bases, namespace):
cls = type("IoC{}".format(name), tuple(), namespace)
return type(name, bases + (cls,), {})
# Entities level
class Entity:
def _lower_level_meth(self):
raise NotImplementedError
@property
def entity_prop(self):
return super(Entity, self)._lower_level_meth()
# Adapters level
class ImplementedEntity(Entity, metaclass=MetaIoC):
__private = 'private attribute value'
def __init__(self, pub_attr):
self.pub_attr = pub_attr
def _lower_level_meth(self):
print('{}\n{}'.format(self.pub_attr, self.__private))
# Infrastructure level
if __name__ == '__main__':
ENTITY = ImplementedEntity('public attribute value')
ENTITY.entity_prop
>>> class OuterClass(object):
... def __init__(self, inner_class):
... self.inner_class = inner_class
...
>>> class InnerClass(object):
... def __init__(self):
... self.forty_two = 42
...
>>> obj_graph = pinject.new_object_graph()
>>> outer_class = obj_graph.provide(OuterClass)
>>> print outer_class.inner_class.forty_two
42
def inject_method_from_module(modulename, methodname):
"""
injects dynamically a method in a module
"""
mod = importlib.import_module(modulename)
return getattr(mod, methodname, None)
myfunction = inject_method_from_module("mypackage.mymodule", "myfunction")
myfunction("a")
def myfunction(s):
print("myfunction in mypackage.mymodule called with parameter:", s)