2020-09-01 23:04:48 +03:00
|
|
|
|
.. _factory-provider:
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Factory provider
|
2020-09-01 23:04:48 +03:00
|
|
|
|
================
|
2015-06-10 09:53:15 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. meta::
|
|
|
|
|
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory,
|
|
|
|
|
Pattern,Example,Aggregate
|
|
|
|
|
:description: Factory provider helps to implement dependency injection in Python. This page
|
|
|
|
|
demonstrates how to use Factory provider, inject the dependencies, and assemble
|
|
|
|
|
object graphs passing the injections deep inside. It also provides the examples
|
|
|
|
|
of the Abstract Factory pattern & provider and Factories Aggregation pattern.
|
2015-11-22 00:59:36 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. currentmodule:: dependency_injector.providers
|
2015-06-10 09:53:15 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:py:class:`Factory` provider creates new objects.
|
2015-06-23 16:21:37 +03:00
|
|
|
|
|
2015-08-03 15:42:44 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/factory.py
|
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2015-06-10 09:53:15 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
The first argument of the ``Factory`` provider is a class, a factory function or a method
|
|
|
|
|
that creates an object.
|
2015-10-19 17:26:59 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
The rest of the ``Factory`` positional and keyword arguments are the dependencies.
|
|
|
|
|
``Factory`` injects the dependencies every time when creates a new object. The dependencies are
|
|
|
|
|
injected following these rules:
|
2015-12-28 18:25:25 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
+ If the dependency is a provider, this provider is called and the result of the call is injected.
|
|
|
|
|
+ If you need to inject the provider itself, you should use the ``.provider`` attribute. More at
|
|
|
|
|
:ref:`factory_providers_delegation`.
|
|
|
|
|
+ All other dependencies are injected *"as is"*.
|
|
|
|
|
+ Positional context arguments are appended after ``Factory`` positional dependencies.
|
|
|
|
|
+ Keyword context arguments have the priority over the ``Factory`` keyword dependencies with the
|
|
|
|
|
same name.
|
2016-06-08 16:39:53 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. image:: images/factory_init_injections.png
|
2015-10-19 17:26:59 +03:00
|
|
|
|
|
2016-06-08 16:39:53 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_init_injections.py
|
2015-08-03 15:42:44 +03:00
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2015-06-10 09:53:15 +03:00
|
|
|
|
|
2021-03-04 00:06:53 +03:00
|
|
|
|
``Factory`` provider can inject attributes. Use ``.add_attributes()`` method to specify
|
|
|
|
|
attribute injections.
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_attribute_injections.py
|
|
|
|
|
:language: python
|
|
|
|
|
:lines: 3-
|
|
|
|
|
:emphasize-lines: 18-18
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Passing arguments to the underlying providers
|
2020-09-03 00:33:02 +03:00
|
|
|
|
---------------------------------------------
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
``Factory`` provider can pass the arguments to the underlying providers. This helps when you need
|
|
|
|
|
to assemble a nested objects graph and pass the arguments deep inside.
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Consider the example:
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. image:: images/factory_init_injections_underlying.png
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
To create an ``Algorithm`` you need to provide all the dependencies: ``ClassificationTask``,
|
|
|
|
|
``Loss``, and ``Regularizer``. The last object in the chain, the ``Regularizer`` has a dependency
|
|
|
|
|
on the ``alpha`` value. The ``alpha`` value varies from algorithm to algorithm:
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. code-block:: python
|
2020-08-06 23:42:31 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Algorithm(
|
|
|
|
|
task=ClassificationTask(
|
|
|
|
|
loss=Loss(
|
|
|
|
|
regularizer=Regularizer(
|
|
|
|
|
alpha=alpha, # <-- the dependency
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
2016-06-08 17:46:40 +03:00
|
|
|
|
|
2015-07-20 18:46:45 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
``Factory`` provider helps to deal with the such assembly. You need to create the factories for
|
|
|
|
|
all the classes and use special double-underscore ``__`` syntax for passing the ``alpha`` argument:
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py
|
2015-08-03 15:42:44 +03:00
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2020-09-03 23:46:03 +03:00
|
|
|
|
:emphasize-lines: 44,49
|
2020-09-01 04:26:21 +03:00
|
|
|
|
|
|
|
|
|
When you use ``__`` separator in the name of the keyword argument the ``Factory`` looks for
|
|
|
|
|
the dependency with the same name as the left part of the ``__`` expression.
|
2015-12-13 15:22:59 +03:00
|
|
|
|
|
2020-09-02 04:58:13 +03:00
|
|
|
|
.. code-block:: none
|
2020-09-01 04:26:21 +03:00
|
|
|
|
|
|
|
|
|
<dependency>__<keyword for the underlying provider>=<value>
|
|
|
|
|
|
|
|
|
|
If ``<dependency>`` is found the underlying provider will receive the
|
|
|
|
|
``<keyword for the underlying provider>=<value>`` as an argument.
|
|
|
|
|
|
|
|
|
|
.. _factory_providers_delegation:
|
2016-06-08 17:46:40 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Passing providers to the objects
|
2020-09-03 00:33:02 +03:00
|
|
|
|
--------------------------------
|
2015-12-13 15:22:59 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
When you need to inject the provider itself, but not the result of its call, use the ``.provider``
|
|
|
|
|
attribute of the provider that you're going to inject.
|
2015-12-13 15:22:59 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. image:: images/factory_delegation.png
|
2015-12-13 15:22:59 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
2015-12-13 15:22:59 +03:00
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2020-09-03 23:46:03 +03:00
|
|
|
|
:emphasize-lines: 28
|
2017-02-28 23:07:12 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. note:: Any provider has a ``.provider`` attribute.
|
2017-04-07 01:00:52 +03:00
|
|
|
|
|
2020-09-01 23:04:48 +03:00
|
|
|
|
.. _factory-specialize-provided-type:
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Specializing the provided type
|
2020-09-03 00:33:02 +03:00
|
|
|
|
------------------------------
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
You can create a specialized ``Factory`` provider that provides only specific type. For doing
|
|
|
|
|
this you need to create a subclass of the ``Factory`` provider and define the ``provided_type``
|
|
|
|
|
class attribute.
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_provided_type.py
|
|
|
|
|
:language: python
|
|
|
|
|
:lines: 3-
|
|
|
|
|
:emphasize-lines: 12-14
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 23:04:48 +03:00
|
|
|
|
.. _abstract-factory:
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Abstract factory
|
2020-09-03 00:33:02 +03:00
|
|
|
|
----------------
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:py:class:`AbstractFactory` provider helps when you need to create a provider of some base class
|
|
|
|
|
and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory``
|
|
|
|
|
provider with two peculiarities:
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
+ Provides only objects of a specified type.
|
|
|
|
|
+ Must be overridden before usage.
|
2017-04-07 00:47:30 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. image:: images/abstract_factory.png
|
2017-04-07 00:47:30 +03:00
|
|
|
|
:width: 100%
|
|
|
|
|
:align: center
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/abstract_factory.py
|
2017-04-07 00:47:30 +03:00
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2020-09-03 23:46:03 +03:00
|
|
|
|
:emphasize-lines: 34
|
2017-02-28 23:07:12 +03:00
|
|
|
|
|
2022-01-10 03:21:31 +03:00
|
|
|
|
.. _factory-aggregate-provider:
|
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
Factory aggregate
|
2020-09-03 00:33:02 +03:00
|
|
|
|
-----------------
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2021-08-25 17:20:45 +03:00
|
|
|
|
:py:class:`FactoryAggregate` provider aggregates multiple factories.
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2022-01-10 05:25:52 +03:00
|
|
|
|
.. seealso::
|
|
|
|
|
:ref:`aggregate-provider` – it's a successor of ``FactoryAggregate`` provider that can aggregate
|
|
|
|
|
any type of provider, not only ``Factory``.
|
|
|
|
|
|
2021-08-25 17:20:45 +03:00
|
|
|
|
The aggregated factories are associated with the string keys. When you call the
|
|
|
|
|
``FactoryAggregate`` you have to provide one of the these keys as a first argument.
|
|
|
|
|
``FactoryAggregate`` looks for the factory with a matching key and calls it with the rest of the arguments.
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. image:: images/factory_aggregate.png
|
|
|
|
|
:width: 100%
|
|
|
|
|
:align: center
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_aggregate.py
|
2017-10-13 07:55:29 +03:00
|
|
|
|
:language: python
|
2020-09-01 04:26:21 +03:00
|
|
|
|
:lines: 3-
|
2020-09-03 23:46:03 +03:00
|
|
|
|
:emphasize-lines: 33-37,47
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2021-11-26 19:50:19 +03:00
|
|
|
|
You can get a dictionary of the aggregated providers using ``.providers`` attribute.
|
|
|
|
|
To get a game provider dictionary from the previous example you can use
|
|
|
|
|
``game_factory.providers`` attribute.
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
2021-08-25 17:20:45 +03:00
|
|
|
|
previous example you can do ``chess = game_factory.chess("John", "Jane")``.
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. note::
|
|
|
|
|
You can not override the ``FactoryAggregate`` provider.
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2020-09-01 04:26:21 +03:00
|
|
|
|
.. note::
|
|
|
|
|
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
2017-10-13 07:55:29 +03:00
|
|
|
|
|
2021-11-26 19:50:19 +03:00
|
|
|
|
To use non-string keys or string keys with ``.`` and ``-``, you can provide a dictionary as a positional argument:
|
2021-08-25 17:20:45 +03:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
providers.FactoryAggregate({
|
|
|
|
|
SomeClass: providers.Factory(...),
|
|
|
|
|
"key.with.periods": providers.Factory(...),
|
|
|
|
|
"key-with-dashes": providers.Factory(...),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: ../../examples/providers/factory_aggregate_non_string_keys.py
|
|
|
|
|
:language: python
|
|
|
|
|
:lines: 3-
|
|
|
|
|
:emphasize-lines: 30-33,39-40
|
|
|
|
|
|
|
|
|
|
|
2017-02-28 23:07:12 +03:00
|
|
|
|
.. disqus::
|