bag*_*rat 63 python oop authentication frameworks dependency-injection
对于Java,依赖注入作为纯OOP工作,即您提供要实现的接口,并在您的框架代码中接受实现已定义接口的类的实例.
现在对于Python,您可以以相同的方式执行,但我认为在Python的情况下,该方法的开销太大了.那么你将如何以Pythonic方式实现它?
说这是框架代码:
class FrameworkClass():
def __init__(self, ...):
...
def do_the_job(self, ...):
# some stuff
# depending on some external function
Run Code Online (Sandbox Code Playgroud)
最天真(也许是最好的?)方式是要求将外部函数提供给FrameworkClass构造函数,然后从该do_the_job方法调用.
框架代码:
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self, ...):
# some stuff
self.func(...)
Run Code Online (Sandbox Code Playgroud)
客户代码:
def my_func():
# my implementation
framework_instance = FrameworkClass(my_func)
framework_instance.do_the_job(...)
Run Code Online (Sandbox Code Playgroud)
问题很简短.有没有更好的常用Pythonic方法来做到这一点?或者也许任何支持此类功能的库?
想象一下,我开发了一个微型Web框架,它使用令牌处理身份验证.该框架需要一个函数来提供ID从令牌获得的一些并获得与之对应的用户ID.
显然,框架对用户或任何其他特定于应用程序的逻辑一无所知,因此客户端代码必须将用户getter功能注入框架以使身份验证工作.
小智 53
见Raymond Hettinger - 超级考虑超级!- PyCon 2015关于如何使用超级和多重继承而不是DI的论点.如果您没有时间观看整个视频,请跳至第15分钟(但我建议您观看所有视频).
以下是如何将此视频中描述的内容应用于您的示例的示例:
框架代码:
class TokenInterface():
def getUserFromToken(self, token):
raise NotImplementedError
class FrameworkClass(TokenInterface):
def do_the_job(self, ...):
# some stuff
self.user = super().getUserFromToken(...)
Run Code Online (Sandbox Code Playgroud)
客户代码:
class SQLUserFromToken(TokenInterface):
def getUserFromToken(self, token):
# load the user from the database
return user
class ClientFrameworkClass(FrameworkClass, SQLUserFromToken):
pass
framework_instance = ClientFrameworkClass()
framework_instance.do_the_job(...)
Run Code Online (Sandbox Code Playgroud)
这将起作用,因为Python MRO将保证调用getUserFromToken客户端方法(如果使用了super()).如果您使用的是Python 2.x,代码将不得不更改.
这里的一个额外好处是,如果客户端不提供实现,这将引发异常.
当然,这不是真正的依赖注入,它是多重继承和mixins,但它是一种解决问题的Pythonic方法.
Pio*_*rek 17
我们在项目中进行依赖注入的方式是使用inject lib.查看文档.我强烈建议将它用于DI.只需要一个函数就没有意义,但是当你必须管理多个数据源等时开始有意义.
按照你的例子,它可能类似于:
# framework.py
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self):
# some stuff
self.func()
Run Code Online (Sandbox Code Playgroud)
你的自定义功能:
# my_stuff.py
def my_func():
print('aww yiss')
Run Code Online (Sandbox Code Playgroud)
在应用程序的某个位置,您要创建一个跟踪所有已定义依赖项的引导文件:
# bootstrap.py
import inject
from .my_stuff import my_func
def configure_injection(binder):
binder.bind(FrameworkClass, FrameworkClass(my_func))
inject.configure(configure_injection)
Run Code Online (Sandbox Code Playgroud)
然后你可以这样使用代码:
# 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()
Run Code Online (Sandbox Code Playgroud)
我担心这会像pythonic一样(模块有一些python的甜味,像装饰器通过参数注入等 - 检查文档),因为python没有像接口或类型提示这样的花哨的东西.
所以直接回答你的问题会非常困难.我认为真正的问题是:python是否对DI有一些原生支持?答案是,遗憾的是:不.
Bry*_*ach 17
依赖注入是 Python 直接支持的一种简单技术。不需要额外的库。使用类型提示可以提高清晰度和可读性。
class UserStore():
"""
The base class for accessing a user's information.
The client must extend this class and implement its methods.
"""
def get_name(self, token):
raise NotImplementedError
class WebFramework():
def __init__(self, user_store: UserStore):
self.user_store = user_store
def greet_user(self, token):
user_name = self.user_store.get_name(token)
print(f'Good day to you, {user_name}!')
Run Code Online (Sandbox Code Playgroud)
class AlwaysMaryUser(UserStore):
def get_name(self, token):
return 'Mary'
class SQLUserStore(UserStore):
def __init__(self, db_params):
self.db_params = db_params
def get_name(self, token):
# TODO: Implement the database lookup
raise NotImplementedError
client = WebFramework(AlwaysMaryUser())
client.greet_user('user_token')
Run Code Online (Sandbox Code Playgroud)
UserStore实现依赖注入不需要类和类型提示。他们的主要目的是为客户端开发人员提供指导。如果删除该类UserStore及其所有引用,该代码仍然有效。
前段时间我编写了依赖注入微框架,其目标是使它成为Pythonic - Dependency Injector.这就是你的代码在使用时的样子:
"""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)
Run Code Online (Sandbox Code Playgroud)
以下是此示例的更广泛描述的链接 - http://python-dependency-injector.ets-labs.org/examples/services_miniapp.html
希望它可以帮助一点.欲了解更多信息,请访问:
小智 5
在尝试了 Python 中的一些 DI 框架之后,我发现与其他领域(例如 .NET Core)中的简单性相比,它们使用起来有点笨拙。这主要是由于通过诸如装饰器之类的东西进行连接,这些东西使代码变得混乱,使得很难简单地将其添加到项目中或从项目中删除它,或者基于变量名称进行连接。
我最近一直在开发一个依赖注入框架,它使用类型注释来进行注入,称为简单注入。下面是一个简单的例子
from simple_injection import ServiceCollection
class Dependency:
def hello(self):
print("Hello from Dependency!")
class Service:
def __init__(self, dependency: Dependency):
self._dependency = dependency
def hello(self):
self._dependency.hello()
collection = ServiceCollection()
collection.add_transient(Dependency)
collection.add_transient(Service)
collection.resolve(Service).hello()
# Outputs: Hello from Dependency!
Run Code Online (Sandbox Code Playgroud)
该库支持服务生命周期并将服务绑定到实现。
该库的目标之一是,可以轻松地将其添加到现有应用程序中,并在提交之前了解您的喜好,因为它所需要的只是您的应用程序具有适当的类型,然后您可以在以下位置构建依赖关系图:入口点并运行它。
希望这可以帮助。欲了解更多信息,请参阅