Injector - Python dependency injection framework, inspired by Guice
===================================================================
[![image](https://secure.travis-ci.org/alecthomas/injector.svg?branch=master)](https://travis-ci.org/alecthomas/injector)
[![Coverage Status](https://coveralls.io/repos/github/alecthomas/injector/badge.svg?branch=master)](https://coveralls.io/github/alecthomas/injector?branch=master)
Introduction
------------
While dependency injection is easy to do in Python due to its support for keyword arguments, the ease with which objects can be mocked and its dynamic natura, a framework for assisting in this process can remove a lot of boiler-plate from larger applications. That's where Injector can help. It automatically and transitively provides dependencies for you. As an added benefit, Injector encourages nicely compartmentalised code through the use of :ref:`modules <module>`.
If you're not sure what dependency injection is or you'd like to learn more about it see:
* [The Clean Code Talks - Don't Look For Things! (a talk by Miško Hevery)](
https://www.youtube.com/watch?v=RlfLCWKxHJ0)
* [Inversion of Control Containers and the Dependency Injection pattern (an article by Martin Fowler)](
https://martinfowler.com/articles/injection.html)
The core values of Injector are:
* Simplicity - while being inspired by Guice, Injector does not slavishly replicate its API.
Providing a Pythonic API trumps faithfulness. Additionally some features are ommitted
because supporting them would be cumbersome and introduce a little bit too much "magic"
(member injection, method injection).
Connected to this, Injector tries to be as nonintrusive as possible. For example while you may
declare a class' constructor to expect some injectable parameters, the class' constructor
remains a standard constructor – you may instaniate the class just the same manually, if you want.
* No global state – you can have as many [Injector](https://injector.readthedocs.io/en/latest/api.html#injector.Injector)
instances as you like, each with a different configuration and each with different objects in different
scopes. Code like this won't work for this very reason:
```python
class MyClass:
@inject
def __init__(t: SomeType):
# ...
MyClass()
```
This is simply because there's no global `Injector` to use. You need to be explicit and use
[Injector.get](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.get),
[Injector.create_object](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.create_object)
or inject `MyClass` into the place that needs it.
* Cooperation with static type checking infrastructure – the API provides as much static type safety
as possible and only breaks it where there's no other option. For example the
[Injector.get](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.get) method
is typed such that `injector.get(SomeType)` is statically declared to return an instance of
`SomeType`, therefore making it possible for tools such as [mypy](https://github.com/python/mypy) to
type-check correctly the code using it.
### How to get Injector?
* GitHub (code repository, issues): https://github.com/alecthomas/injector
* PyPI (installable, stable distributions): https://pypi.python.org/pypi/injector. You can install it using pip:
```bash
pip install injector
```
* Documentation: http://injector.readthedocs.org
* Change log: http://injector.readthedocs.io/en/latest/changelog.html
Injector works with CPython 3.5+ and PyPy 3 implementing Python 3.5+.
A Quick Example
---------------
```python
>>> from injector import Injector, inject
>>> class Inner:
... def __init__(self):
... self.forty_two = 42
...
>>> class Outer:
... @inject
... def __init__(self, inner: Inner):
... self.inner = inner
...
>>> injector = Injector()
>>> outer = injector.get(Outer)
>>> outer.inner.forty_two
42
```
Or with `dataclasses` if you like:
```python
from dataclasses import dataclass
from injector import Injector, inject
class Inner:
def __init__(self):
self.forty_two = 42
@inject
@dataclass
class Outer:
inner: Inner
injector = Injector()
outer = injector.get(Outer)
print(outer.inner.forty_two) # Prints 42
```
A Full Example
--------------
Here's a full example to give you a taste of how Injector works:
```python
>>> from injector import Module, provider, Injector, inject, singleton
```
We'll use an in-memory SQLite database for our example:
```python
>>> import sqlite3
```
And make up an imaginary `RequestHandler` class that uses the SQLite connection:
```python
>>> class RequestHandler:
... @inject
... def __init__(self, db: sqlite3.Connection):
... self._db = db
...
... def get(self):
... cursor = self._db.cursor()
... cursor.execute('SELECT key, value FROM data ORDER by key')
... return cursor.fetchall()
```
Next, for the sake of the example, we'll create a configuration type:
```python
>>> class Configuration:
... def __init__(self, connection_string):
... self.connection_string = connection_string
```
Next, we bind the configuration to the injector, using a module:
```python
>>> def configure_for_testing(binder):
... configuration = Configuration(':memory:')
... binder.bind(Configuration, to=configuration, scope=singleton)
```
Next we create a module that initialises the DB. It depends on the configuration provided by the above module to create a new DB connection, then populates it with some dummy data, and provides a `Connection` object:
```python
>>> class DatabaseModule(Module):
... @singleton
... @provider
... def provide_sqlite_connection(self, configuration: Configuration) -> sqlite3.Connection:
... conn = sqlite3.connect(configuration.connection_string)
... cursor = conn.cursor()
... cursor.execute('CREATE TABLE IF NOT EXISTS data (key PRIMARY KEY, value)')
... cursor.execute('INSERT OR REPLACE INTO data VALUES ("hello", "world")')
... return conn
```
(Note how we have decoupled configuration from our database initialisation code.)
Finally, we initialise an `Injector` and use it to instantiate a `RequestHandler` instance. This first transitively constructs a `sqlite3.Connection` object, and the Configuration dictionary that it in turn requires, then instantiates our `RequestHandler`:
```python
>>> injector = Injector([configure_for_testing, DatabaseModule()])
>>> handler = injector.get(RequestHandler)
>>> tuple(map(str, handler.get()[0])) # py3/py2 compatibility hack
('hello', 'world')
```
We can also verify that our `Configuration` and `SQLite` connections are indeed singletons within the Injector:
```python
>>> injector.get(Configuration) is injector.get(Configuration)
True
>>> injector.get(sqlite3.Connection) is injector.get(sqlite3.Connection)
True
```
You're probably thinking something like: "this is a large amount of work just to give me a database connection", and you are correct; dependency injection is typically not that useful for smaller projects. It comes into its own on large projects where the up-front effort pays for itself in two ways:
1. Forces decoupling. In our example, this is illustrated by decoupling our configuration and database configuration.
2. After a type is configured, it can be injected anywhere with no additional effort. Simply `@inject` and it appears. We don't really illustrate that here, but you can imagine adding an arbitrary number of `RequestHandler` subclasses, all of which will automatically have a DB connection provided.
Footnote
--------
This framework is similar to snake-guice, but aims for simplification.
© Copyright 2010-2013 to Alec Thomas, under the BSD license
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
共12个文件
txt:4个
pkg-info:2个
py:2个
资源分类:Python库 所属语言:Python 资源全名:injector-0.18.2.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
资源推荐
资源详情
资源评论
收起资源包目录
injector-0.18.2.tar.gz (12个子文件)
injector-0.18.2
MANIFEST.in 18B
PKG-INFO 10KB
injector.egg-info
PKG-INFO 10KB
requires.txt 25B
SOURCES.txt 238B
top_level.txt 9B
dependency_links.txt 1B
setup.cfg 64B
injector
py.typed 0B
__init__.py 49KB
setup.py 2KB
README.md 8KB
共 12 条
- 1
资源评论
挣扎的蓝藻
- 粉丝: 13w+
- 资源: 15万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功