前言
在 Java Spring Boot 等等的后端领域,会大量使用依赖注入的方式来简化复杂的设计模式。
实现参数的自动化注入。
这些设计方式在 Python 的世界里使用不多,因为 Python 语言足够灵活。
倘若需要开发复杂的框架,使用 依赖注入 框架可以简化很多代码。
Github 地址
官方说明文档
依赖注入解决的问题
参考文章
在日常开发中,我们的方法调用可能会越来越深。
1 2 3 4 5 6 7 8 9 10
| def create_robot(robot_name): create_robot_hand() def create_robot_hand(): create_robot_finger()
def create_robot_finger(): print("create_robot_finger")
|
上面是一个简单的机器人创建调用函数。
调用方式会伴随则系统的复杂程度逐层深入。
到了 create_robot_finger
深度的时候,可能会需要在上层传入参数控制 finger 的数量
1 2 3 4 5 6 7 8 9
| def create_robot(robot_name,finger_num=10): create_robot_hand(finger_num=finger_num) def create_robot_hand(finger_num=10): create_robot_finger(finger_num=finger_num)
def create_robot_finger(finger_num=10): print("create_robot_finger finder_number:{0}".format(finger_num))
|
这需要将参数补充到 调用链条 的每一个函数当中。
如果只是上面的 三层 调用深度,那可能手动修改维护还不是什么问题。
但倘若调用深度很深,那这个代码修改量就会非常庞大。
不利于代码的扩展和维护。
在 Python 的世界里,解决这个问题的方法有很多。
- 导入 配置 模块,外部获取参数配置
- 面向对象 注入依赖,从实例化中获取参数配置
方案一 导入模块
1 2 3 4 5 6 7 8
| """settings.py"""
from __future__ import division from __future__ import print_function from __future__ import absolute_import
ROBOT_FINGER_NUM = 10
|
1 2 3 4 5 6 7 8 9 10 11
| import settings
def create_robot(robot_name): create_robot_hand() def create_robot_hand(): create_robot_finger()
def create_robot_finger(): print("create_robot_finger finder_number:{0}".format(settings.ROBOT_FINGER_NUM))
|
通过模块的方式将参数转移到外部,进行配置。
这个做法可以解决参数传递的问题。
缺点就是参数管理会比较麻烦,通常是将全局配置的参数都放到一个文件方便集中管理。
但是这样会导致不同的逻辑调用的参数都会塞到一个文件里面,并不是十分整洁。
方案二 注入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| from dependencies import Injector import attr
@attr.s class Robot(object): finger_num = attr.ib(default=10)
def create_robot(self,robot_name): self.create_robot_hand()
def create_robot_hand(self): self.create_robot_finger()
def create_robot_finger(self): print("create_robot_finger finder_number:{0}".format(self.finger_num))
class Container(Injector): finger_num = 10 robot = Robot Container.robot.create_robot("robot name")
robot = Robot(finger_num=10) robot.create_robot("robot name")
|
使用 dependencies
库实现依赖注入,自动将容器内的数据填充到 类的实例化过程中。
通过类的属性实现参数传递。
dependencies 介绍
通过上面的案例可以看到。
dependencies
可以自动实例化类,填充类初始化需要的参数。
但它的功能还远不止这么简单。
它还可以实现多个类实例化的自动填充,只要参数变量名命名配置好即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| from dependencies import Injector import attr
@attr.s class Robot(object): servo = attr.ib() controller = attr.ib() settings = attr.ib() di_environment = attr.ib()
def run(self): print("controller di_environment",self.controller.di_environment) print("self di_environment",self.di_environment) print("settings threshold",self.settings.threshold) print("servo threshold",self.servo.threshold)
@attr.s class Servo(object): threshold = attr.ib() @attr.s class Controller(object): di_environment = attr.ib()
@attr.s class Settings(object): threshold = attr.ib()
class Container(Injector): threshold = 1 di_environment = "production" robot = Robot servo = Servo settings = Settings controller = Controller
Container.robot.run()
|
通过 dependencies
可以根据属性命名自动填充多个类的参数数据。
container 的逻辑等价于下面的代码
1 2 3 4 5 6 7 8 9 10 11 12
| threshold = 1 di_environment = "production" servo = Servo(threshold) settings = Settings(threshold) controller = Controller(di_environment) robot = Robot(servo,controller,settings,di_environment) robot.run()
|
但是 dependencies
库根据参数的命名自动实例化对象,参数的调整变得简单可控。
dependencies 实现 caller 方法
参考文章
利用 依赖注入 可以分离 依赖 和 业务 逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import attr from dependencies import Injector
class Editor(object): def install_language(self,lang): print("install language:{0}".format(lang))
editor = Editor()
@attr.s(frozen=True, slots=True) class ChangeLanguage(object): editor = attr.ib()
def __call__(self,lang): self.editor.install_language(lang)
class Container(Injector): editor = editor change_language = ChangeLanguage
Container.change_language("en_US")
|
利用 dependencies 可以构建出 caller 对象。
caller 虽然用类构建,但是调用方式和方法一致,可以方法需要用到的依赖用类实例化的方式进行注入。
实现依赖和传参的分离。
总结
依赖注入可以很好解决函数调用过深的问题,让代码结构更加清晰。