百科.dev
全部条目在线工具AI 编程趋势榜开源项目技术资讯提交条目
登录
< 返回工具列表
P

pinject

> 编程语言
开源

一个符合 Python 编程风格的依赖注入库。

1.3K stars0 点赞0 次浏览
访问官网GitHub

工具介绍

一个符合 Python 编程风格的依赖注入库。

========= Pinject

.. image:: https://badge.fury.io/py/pinject.svg :target: https://pypi.org/project/pinject/ .. image:: https://github.com/google/pinject/workflows/PyPI/badge.svg :target: https://github.com/google/pinject/actions?query=workflow%3APyPI .. image:: https://pepy.tech/badge/pinject :target: https://pepy.tech/badge/pinject .. image:: https://pepy.tech/badge/pinject/month :target: https://pepy.tech/badge/pinject

Pinject is a dependency injection library for python.

The primary goal of Pinject is to help you assemble objects into graphs in an easy, maintainable way.

If you are already familiar with other dependency injection libraries, you may want to read the condensed summary section at the end, so that you get an idea of what Pinject is like and how it might differ from libraries you're used to.

There is a changelog of differences between released versions near the end of this README.

Why Pinject?

If you're wondering why to use a dependency injection library at all: if you're writing a lot of object-oriented code in python, then it will make your life easier. See, for instance:

  • https://en.wikipedia.org/wiki/Dependency_injection
  • http://lmgtfy.com/?q=dependency+injection

If you're wondering why to use Pinject instead of another python dependency injection library, a few of reasons are:

  • Pinject is much easier to get started with. Forget having to decorate your code with @inject_this and @annotate_that just to get started. With Pinject, you call new_object_graph(), one line, and you're good to go.
  • Pinject is a pythonic dependency injection library. Python ports of other libraries, like Spring or Guice, retain the feel (and verbosity) of being designed for a statically typed language. Pinject is designed from the ground up for python.
  • The design choices in Pinject are informed by several dependency injection experts working at Google, based on many years of experience. Several common confusing or misguided features are omitted altogether from Pinject.
  • Pinject has great error messages. They tell you exactly what you did wrong, and exactly where. This should be a welcome change from other dependency frameworks, with their voluminous and yet inscrutable stack traces.

Look at the simplest getting-started examples for Pinject and for other similar libraries. Pinject should be uniformly easier to use, clearer to read, and less boilerplate that you need to add. If you don't find this to be the case, email!

Installation

The easiest way to install Pinject is to get the latest released version from PyPI:

.. code-block:: shell

bash
sudo pip install pinject

If you are interested in the developing version, you can install the next version from Test PyPI:

.. code-block:: shell

bash
sudo pip install \
    --no-deps \
    --no-cache \
    --upgrade \
    --index-url https://test.pypi.org/simple/ \
    pinject

You can also check out all the source code, including tests, designs, and TODOs:

.. code-block:: shell

git clone https://github.com/google/pinject

Basic dependency injection

The most important function in the pinject module is new_object_graph(). This creates an ObjectGraph, which you can use to instantiate objects using dependency injection. If you pass no args to new_object_graph(), it will return a reasonably configured default ObjectGraph.

.. code-block:: 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
>>>

As you can see, you don't need to tell Pinject how to construct its ObjectGraph, and you don't need to put decorators in your code. Pinject has reasonable defaults that allow it to work out of the box.

A Pinject binding is an association between an arg name and a provider. In the example above, Pinject created a binding between the arg name inner_class and an implicitly created provider for the class InnerClass. The binding it had created was how Pinject knew that it should pass an instance of InnerClass as the value of the inner_class arg when instantiating OuterClass.

Implicit class bindings

Pinject creates implicit bindings for classes. The implicit bindings assume your code follows PEP8 conventions: your classes are named in CamelCase, and your args are named in lower_with_underscores. Pinject transforms class names to injectable arg names by lowercasing words and connecting them with underscores. It will also ignore any leading underscore on the class name.

+-------------+-------------+ | Class name | Arg name | +=============+=============+ | Foo | foo | +-------------+-------------+ | FooBar | foo_bar | +-------------+-------------+ | _Foo | foo | +-------------+-------------+ | _FooBar | foo_bar | +-------------+-------------+

If two classes map to the same arg name, whether those classes are in the same module or different modules, Pinject will not create an implicit binding for that arg name (though it will not raise an error).

Finding classes and providers for implicit bindings

So far, the examples have not told new_object_graph() the classes for which it should create implicit bindings. new_object_graph() by default looks in all imported modules, but you may occasionally want to restrict the classes for which new_object_graph() creates implicit bindings. If so, new_object_graph() has two args for this purpose.

  • The modules arg specifies in which (python) modules to look for classes; this defaults to ALL_IMPORTED_MODULES.
  • The classes arg specifies a exact list of classes; this defaults to None.

.. code-block:: python

>>> class SomeClass(object):
...     def __init__(self, foo):
...         self.foo = foo
...
>>> class Foo(object):
...     pass
...
>>> obj_graph = pinject.new_object_graph(modules=None, classes=[SomeClass])
>>> # obj_graph.provide(SomeClass)  # would raise a NothingInjectableForArgError
>>> obj_graph = pinject.new_object_graph(modules=None, classes=[SomeClass, Foo])
>>> some_class = obj_graph.provide(SomeClass)
>>>

Auto-copying args to fields

One thing that can get tedious about dependency injection via initializers is that you need to write __init__() methods that copy args to fields. These __init__() methods can get repetitive, especially when you have several initializer args.

.. code-block:: python

>>> class ClassWithTediousInitializer(object):
...     def __init__(self, foo, bar, baz, quux):
...         self._foo = foo
...         self._bar = bar
...         self._baz = baz
...         self._quux = quux
...
>>>

Pinject provides decorators that you can use to avoid repetitive initializer bodies.

  • @copy_args_to_internal_fields prepends an underscore, i.e., it copies an arg named foo to a field named _foo. It's useful for normal classes.
  • @copy_args_to_public_fields copies the arg named as-is, i.e., it copies an arg named foo to a field named foo. It's useful for data objects.

.. code-block:: python

>>> class ClassWithTediousInitializer(object):
...     @pinject.copy_args_to_internal_fields
...     def __init__(self, foo, bar, baz, quux):
...         pass
...
>>> cwti = ClassWithTediousInitializer('a-foo', 'a-bar', 'a-baz', 'a-quux')
>>> print cwti._foo
'a-foo'
>>>

When using these decorators, you'll normally pass in the body of the initializer, but you can put other statements there if you need to. The args will be copied to fields before the initializer body is executed.

These decorators can be applied to initializers that take **kwargs but not initializers that take *pargs (since it would be unclear what field name to use).

Binding specs

To create any bindings more complex than the implicit class bindings described above, you use a binding spec. A binding spec is any python class that inherits from BindingSpec. A binding spec can do three things:

  • Its configure() method can create explicit bindings to classes or instances, as well as requiring bindings without creating them.
  • Its dependencies() method can return depended-on binding specs.
  • It can have provider methods, for which explicit bindings are created.

The new_object_graph() function takes a sequence of binding spec instances as its binding_specs arg. new_object_graph() takes binding spec instances, rather than binding spec classes, so that you can manually inject any initial dependencies into the binding specs as needed.

Binding specs should generally live in files named binding_specs.py, where each file is named in the plural even if there is exactly one binding spec in it. Ideally, a directory's worth of functionality should be coverable with a single binding spec. If not, you can create multiple binding specs in the same binding_specs.py file. If you have so many binding specs that you need to split them into multiple files, you should name them each with a _binding_specs.py suffix.

Binding spec configure() methods

Pinject creates implicit bindings for classes, but sometimes the implicit bindings aren't what you want. For instance, if you have SomeReallyLongClassName, you may not want to name your initializer args some_really_long_class_name but instead use something shorter like long_name, just for this class.

For such situations, you can create explicit bindings using the configure() method of a binding spec. The configure() method takes a function bind() as an arg and calls that function to create explicit bindings.

.. code-block:: python

>>> class SomeClass(object):
...     def __init__(self, long_name):
...         self.long_name = long_name
...
>>> class SomeReallyLongClassName(object):
...     def __init__(self):
...         self.foo = 'foo'
...
>>> class MyBindingSpec(pinject.BindingSpec):
...     def configure(self, bind):
...         bind('long_name', to_class=SomeReallyLongClassName)
...
>>> obj_graph = pinject.new_object_graph(binding_specs=[MyBindingSpec()])
>>> some_class = obj_graph.provide(SomeClass)
>>> print some_class.long_name.foo
'foo'
>>>

The bind() function passed to a binding function binds its first arg, which must be an arg name (as a str), to exactly one of two kinds of things.

  • Using to_class binds to a class. When the binding is used, Pinject injects an instance of the class.
  • Using to_instance binds to an instance of some object. Every time the binding is used, Pinject uses that instance.

.. code-block:: python

>>> class SomeClass(object):
...     def __init__(self, foo):
...         self.foo = foo
...
>>> class MyBindingSpec(pinject.BindingSpec):
...     def configure(self, bind):
...         bind('foo', to_instance='a-foo')
...
>>> obj_graph = pinject.new_object_graph(binding_specs=[MyBindingSpec()])
>>> some_class = obj_graph.provide(SomeClass)
>>> print some_class.foo
'a-foo'
>>>

The configure() method of a binding spec also may take a function require() as an arg and use that function to require that a binding be present without actually defining that binding. require() takes as args the name of the arg

Issues· 0 开放

查看全部 Issues在 GitHub 打开

暂无开放 Issues,或尚未同步最近议题。

> 标签

Python

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月17日
分类编程语言
定价开源

> 相关工具

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言