Skip to content

Latest commit

 

History

History
203 lines (143 loc) · 6.72 KB

File metadata and controls

203 lines (143 loc) · 6.72 KB

Factory provider

.. currentmodule:: dependency_injector.providers

:py:class:`Factory` provider creates new objects.

.. literalinclude:: ../../examples/providers/factory.py
   :language: python
   :lines: 3-

The first argument of the Factory provider is a class, a factory function or a method that creates an object.

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:

  • 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.

images/factory_init_injections.png

.. literalinclude:: ../../examples/providers/factory_init_injections.py
   :language: python
   :lines: 3-

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

Passing arguments to the underlying providers

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.

Consider the example:

images/factory_init_injections_underlying.png

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:

Algorithm(
    task=ClassificationTask(
        loss=Loss(
            regularizer=Regularizer(
                alpha=alpha,  # <-- the dependency
            ),
        ),
    ),
)

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
   :language: python
   :lines: 3-
   :emphasize-lines: 44,49

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.

<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.

Passing providers to the objects

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.

images/factory_delegation.png

.. literalinclude:: ../../examples/providers/factory_delegation.py
   :language: python
   :lines: 3-
   :emphasize-lines: 28

Note

Any provider has a .provider attribute.

Specializing the provided type

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.

.. literalinclude:: ../../examples/providers/factory_provided_type.py
   :language: python
   :lines: 3-
   :emphasize-lines: 12-14

Abstract factory

: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:

  • Provides only objects of a specified type.
  • Must be overridden before usage.

images/abstract_factory.png

.. literalinclude:: ../../examples/providers/abstract_factory.py
   :language: python
   :lines: 3-
   :emphasize-lines: 34

Factory aggregate

:py:class:`FactoryAggregate` provider aggregates multiple factories.

.. seealso::
   :ref:`aggregate-provider` – it's a successor of ``FactoryAggregate`` provider that can aggregate
   any type of provider, not only ``Factory``.

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.

images/factory_aggregate.png

.. literalinclude:: ../../examples/providers/factory_aggregate.py
   :language: python
   :lines: 3-
   :emphasize-lines: 33-37,47

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.

You can also access an aggregated factory as an attribute. To create the Chess object from the previous example you can do chess = game_factory.chess("John", "Jane").

Note

You can not override the FactoryAggregate provider.

Note

When you inject the FactoryAggregate provider it is passed "as is".

To use non-string keys or string keys with . and -, you can provide a dictionary as a positional argument:

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


.. disqus::