Skip to main content
Version: Next

Instantiating objects with Hydra

 Example (Click Here)

One of the best ways to drive different behavior in an application is to instantiate different implementations of an interface. The code using the instantiated object only knows the interface which remains constant, but the behavior is determined by the actual object instance.

Hydra provides hydra.utils.instantiate() (and its alias hydra.utils.call()) for instantiating objects and calling functions. Prefer instantiate for creating objects and call for invoking functions.

Call/instantiate supports:

  • Constructing an object by calling the __init__ method
  • Calling functions, static functions, class methods and other callable global objects

Top-level list and tuple inputs are instantiated element by element. Partial instantiation is not supported for these sequence inputs.

Instantiate API (Expand for details)
def instantiate(config: Any, *args: Any, **kwargs: Any) -> Any:
"""
:param config: An config object describing what to call and what params to use.
In addition to the parameters, the config must contain:
_target_ : target class or callable name (str)
And may contain:
_args_: List-like of positional arguments to pass to the target
_recursive_: Construct nested objects as well (bool).
True by default.
may be overridden via a _recursive_ key in
the kwargs
_convert_: Conversion strategy
none : Passed objects are DictConfig, ListConfig and
TupleConfig, default
partial : Passed objects are converted to dict, list and
tuple, with the exception of Structured Configs
(and their fields).
object : Passed objects are converted to dict, list and tuple.
Structured Configs are converted to instances of the
backing dataclass / attr class.
all : Passed objects are dicts, lists, tuples and
primitives without a trace of OmegaConf containers.
Structured configs are converted to primitive
containers too.
_partial_: If True, return functools.partial wrapped method or object
False by default. Configure per target.
:param _target_whitelist_: A target string, list of target strings,
object returned by target_whitelist(), or
UNSAFE_ALLOW_ALL_TARGETS. Passing None
preserves legacy behavior unless a
target_whitelist() context is active.
:param args: Optional positional parameters pass-through
:param kwargs: Optional named parameters to override
parameters in the config object. Parameters not present
in the config objects are being passed as is to the target.
IMPORTANT: dataclasses instances in kwargs are interpreted as config
and cannot be used as passthrough
:return: if _target_ is a class name: the instantiated object
if _target_ is a callable: the return value of the call
"""

# Alias for instantiate
call = instantiate

Target whitelist API (Expand for details)
def target_whitelist(
target_whitelist: str | Sequence[str] | UNSAFE_ALLOW_ALL_TARGETS | None,
reset: bool = False,
):
"""
Create a target whitelist object for hydra.utils.instantiate().

The returned object can be used as a context manager to apply a whitelist
to instantiate() calls in the current context, or passed to instantiate()
as _target_whitelist_.

:param target_whitelist: A target string, list of target strings, or
UNSAFE_ALLOW_ALL_TARGETS. A trailing .*
allows targets under a package prefix.
:param reset: If True, ignore any outer target_whitelist() context.
If False, add these targets to the current context.
"""

The config passed to these functions must have a key called _target_, with the value of a fully qualified class name, class method, static method or callable. For convenience, None config results in a None object.

Create a whitelist object with target_whitelist(). Use it as a context manager to apply a whitelist to every instantiate() call in a block. This is useful when another function or framework calls instantiate() internally, or when calling instantiate() multiple times with the same whitelist:

from hydra.utils import instantiate, target_whitelist

with target_whitelist("my_app.*"):
framework_function(cfg)

Or pass it directly to one instantiate() call:

model = instantiate(
cfg.model,
_target_whitelist_=target_whitelist("my_app.models.*"),
)

Nested target_whitelist() scopes stack by default: an inner scope adds its targets to the outer scope.

with target_whitelist("my_app.models.*"):
with target_whitelist("my_app.optimizers.*"):
train(cfg)

Use reset=True when the inner scope should replace the outer scope instead of adding to it:

with target_whitelist("my_app.*"):
with target_whitelist("my_app.models.*", reset=True):
instantiate(cfg.model)

For simple direct calls, _target_whitelist_ also accepts a string or list of strings.

The wildcard * by itself is not allowed as a whitelist entry. To explicitly preserve legacy all-target behavior, use UNSAFE_ALLOW_ALL_TARGETS:

from hydra.utils import UNSAFE_ALLOW_ALL_TARGETS, instantiate

component = instantiate(
cfg.component,
_target_whitelist_=UNSAFE_ALLOW_ALL_TARGETS,
)

Named arguments : Config fields (except reserved fields like _target_) are passed as named arguments to the target. Named arguments in the config can be overridden by passing named argument with the same name in the instantiate() call-site.

Call-site arguments are applied separately from the input configuration. They replace the corresponding arguments passed to the target without modifying the input configuration. They therefore do not affect interpolation resolution or Structured Config coercion in the input configuration. Configuration values are resolved lazily as instantiation proceeds instead of resolving and copying the full configuration tree up front. This avoids processing unrelated configuration values and allows runtime state established by an earlier target to be used while resolving a later argument.

See the Hydra 1.4 upgrade guide for the compatibility impact and an example.

Positional arguments : The config may contain a _args_ field representing positional arguments to pass to the target. The positional arguments can be overridden together by passing positional arguments in the instantiate() call-site.

Simple usage

Your application might have an Optimizer class:

Example class
class Optimizer:
algo: str
lr: float

def __init__(self, algo: str, lr: float) -> None:
self.algo = algo
self.lr = lr
Config
optimizer:
_target_: my_app.Optimizer
algo: SGD
lr: 0.01
Instantiation
with target_whitelist("my_app.*"):
opt = instantiate(cfg.optimizer)
print(opt)
# Optimizer(algo=SGD,lr=0.01)

You can override parameters at the call-site:

with target_whitelist("my_app.*"):
opt = instantiate(
cfg.optimizer,
lr=0.2,
)
print(opt)
# Optimizer(algo=SGD,lr=0.2)

Recursive instantiation

Let's add a Dataset and a Trainer class. The trainer holds a Dataset and an Optimizer instances.

Additional classes
class Dataset:
name: str
path: str

def __init__(self, name: str, path: str) -> None:
self.name = name
self.path = path


class Trainer:
def __init__(self, optimizer: Optimizer, dataset: Dataset) -> None:
self.optimizer = optimizer
self.dataset = dataset

With the following config, you can instantiate the whole thing with a single call:

Example config
trainer:
_target_: my_app.Trainer
optimizer:
_target_: my_app.Optimizer
algo: SGD
lr: 0.01
dataset:
_target_: my_app.Dataset
name: Imagenet
path: /datasets/imagenet

Hydra will instantiate nested objects recursively by default.

with target_whitelist("my_app.*"):
trainer = instantiate(cfg.trainer)
print(trainer)
# Trainer(
# optimizer=Optimizer(algo=SGD,lr=0.01),
# dataset=Dataset(name=Imagenet, path=/datasets/imagenet)
# )

You can override parameters for nested objects:

with target_whitelist("my_app.*"):
trainer = instantiate(
cfg.trainer,
optimizer={"lr": 0.3},
dataset={"name": "cifar10", "path": "/datasets/cifar10"},
)
print(trainer)
# Trainer(
# optimizer=Optimizer(algo=SGD,lr=0.3),
# dataset=Dataset(name=cifar10, path=/datasets/cifar10)
# )

Similarly, positional arguments of nested objects can be overridden:

with target_whitelist("my_app.*"):
obj = instantiate(
cfg.object,
# pass 1 and 2 as positional arguments
# to the target object
1, 2,
# pass 3 and 4 as positional arguments
# to a nested child object
child={"_args_": [3, 4]},
)

Disable recursive instantiation

You can disable recursive instantiation by setting _recursive_ to False in the config node or in the call-site In that case the Trainer object will receive an OmegaConf DictConfig for nested dataset and optimizer instead of the instantiated objects.

trainer = instantiate(
cfg.trainer,
_recursive_=False,
_target_whitelist_="my_app.Trainer",
)
print(trainer)

Output:

Trainer(
optimizer={
'_target_': 'my_app.Optimizer', 'algo': 'SGD', 'lr': 0.01
},
dataset={
'_target_': 'my_app.Dataset', 'name': 'Imagenet', 'path': '/datasets/imagenet'
}
)

Parameter conversion strategies

By default, the parameters passed to the target are either primitives (int, float, bool etc) or OmegaConf containers (DictConfig, ListConfig, TupleConfig). OmegaConf containers have many advantages over primitive dicts, lists, and tuples, including convenient attribute access for keys, duck-typing as instances of dataclasses or attrs classes, and support for variable interpolation and custom resolvers. If the callable targeted by instantiate leverages OmegaConf's features, it will make sense to pass DictConfig, ListConfig, and TupleConfig instances directly to that callable.

That being said, in many cases it's desired to pass normal Python dicts and lists and tuples, rather than DictConfig, ListConfig, or TupleConfig instances, as arguments to your callable. You can change instantiate's argument conversion strategy using the _convert_ parameter. Supported values are:

  • "none" : Default behavior, Use OmegaConf containers
  • "partial" : Convert OmegaConf containers to dict, list, and tuple, except Structured Configs, which remain as DictConfig instances.
  • "object" : Convert OmegaConf containers to dict, list, and tuple, except Structured Configs, which are converted to instances of the backing dataclass / attr class using OmegaConf.to_object.
  • "all" : Convert everything to primitive containers

The conversion strategy applies recursively to all subconfigs of the instantiation target. Here is an example demonstrating the various conversion strategies:

from dataclasses import dataclass
from omegaconf import DictConfig, OmegaConf
from hydra.utils import instantiate

@dataclass
class Foo:
a: int = 123

class MyTarget:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar

cfg = OmegaConf.create(
{
"_target_": "__main__.MyTarget",
"foo": Foo(),
"bar": {"b": 456},
}
)

obj_none = instantiate(cfg, _convert_="none", _target_whitelist_="__main__.*")
assert isinstance(obj_none, MyTarget)
assert isinstance(obj_none.foo, DictConfig)
assert isinstance(obj_none.bar, DictConfig)

obj_partial = instantiate(cfg, _convert_="partial", _target_whitelist_="__main__.*")
assert isinstance(obj_partial, MyTarget)
assert isinstance(obj_partial.foo, DictConfig)
assert isinstance(obj_partial.bar, dict)

obj_object = instantiate(cfg, _convert_="object", _target_whitelist_="__main__.*")
assert isinstance(obj_object, MyTarget)
assert isinstance(obj_object.foo, Foo)
assert isinstance(obj_object.bar, dict)

obj_all = instantiate(cfg, _convert_="all", _target_whitelist_="__main__.*")
assert isinstance(obj_all, MyTarget)
assert isinstance(obj_all.foo, dict)
assert isinstance(obj_all.bar, dict)

Passing the _convert_ keyword argument to instantiate has the same effect as defining a _convert_ attribute on your config object. Here is an example creating instances of MyTarget that are equivalent to the above:

cfg_none = OmegaConf.create({..., "_convert_": "none"})
obj_none = instantiate(cfg_none, _target_whitelist_="__main__.*")

cfg_partial = OmegaConf.create({..., "_convert_": "partial"})
obj_partial = instantiate(cfg_partial, _target_whitelist_="__main__.*")

cfg_object = OmegaConf.create({..., "_convert_": "object"})
obj_object = instantiate(cfg_object, _target_whitelist_="__main__.*")

cfg_all = OmegaConf.create({..., "_convert_": "all"})
obj_all = instantiate(cfg_all, _target_whitelist_="__main__.*")

Partial Instantiation

Sometimes you may not set all parameters needed to instantiate an object from the configuration, in this case you can set _partial_ to be True to get a functools.partial wrapped object or method, then complete initializing the object in the application code. Here is an example:

Example classes
class Optimizer:
algo: str
lr: float

def __init__(self, algo: str, lr: float) -> None:
self.algo = algo
self.lr = lr

def __repr__(self) -> str:
return f"Optimizer(algo={self.algo},lr={self.lr})"


class Model:
def __init__(self, optim_partial: Any, lr: float):
super().__init__()
self.optim = optim_partial(lr=lr)
self.lr = lr

def __repr__(self) -> str:
return f"Model(Optimizer={self.optim},lr={self.lr})"
Config
model:
_target_: my_app.Model
optim_partial:
_partial_: true
_target_: my_app.Optimizer
algo: SGD
lr: 0.01
Instantiation
with target_whitelist("my_app.*"):
model = instantiate(cfg.model)
print(model)
# "Model(Optimizer=Optimizer(algo=SGD,lr=0.01),lr=0.01)

If you are repeatedly instantiating the same config, using _partial_=True may provide a significant speedup as compared with regular (non-partial) instantiation.

factory = instantiate(
config,
_partial_=True,
_target_whitelist_="my_app.*",
)
obj = factory()

In the above example, repeatedly calling factory would be faster than repeatedly calling instantiate(config). A caveat of this approach is that the same keyword arguments would be re-used in each call to factory.

class Foo:
...

class Bar:
def __init__(self, foo):
self.foo = foo

bar_conf = {
"_target_": "__main__.Bar",
"foo": {"_target_": "__main__.Foo"},
}

bar_factory = instantiate(
bar_conf,
_partial_=True,
_target_whitelist_="__main__.*",
)
bar1 = bar_factory()
bar2 = bar_factory()

assert bar1 is not bar2
assert bar1.foo is bar2.foo # the `Foo` instance is re-used here

This does not apply if _partial_=False, in which case a new Foo instance would be created with each call to instantiate.

Instantiation of builtins

The value of _target_ passed to instantiate should be a "dotpath" pointing to some callable that can be looked up via a combination of import and getattr. If you want to target one of Python's built-in functions (such as len or print or divmod), you will need to provide a dotpath looking up that function in Python's builtins module.

from hydra.utils import instantiate
# instantiate({"_target_": "len"}, [1,2,3]) # this gives an InstantiationException
instantiate(
{"_target_": "builtins.len"},
[1,2,3],
_target_whitelist_="builtins.len",
) # this works, returns the number 3

Dotpath lookup machinery

Hydra looks up a given _target_ by attempting to find a module that corresponds to a prefix of the given dotpath and then looking for an object in that module corresponding to the dotpath's tail. For example, to look up a _target_ given by the dotpath "my_module.my_nested_module.my_object", hydra first locates the module my_module.my_nested_module, then find my_object inside that nested module.

Hydra exposes an API allowing direct use of this dotpath lookup machinery. The following three functions, which can be imported from the hydra.utils module, accept a string-typed dotpath as an argument and return the located class/callable/object:

def get_class(path: str) -> type:
"""
Look up a class based on a dotpath.
Fails if the path does not point to a class.

>>> import my_module
>>> from hydra.utils import get_class
>>> assert get_class("my_module.MyClass") is my_module.MyClass
"""
...

def get_method(path: str) -> Callable[..., Any]:
"""
Look up a callable based on a dotpath.
Fails if the path does not point to a callable object.

>>> import my_module
>>> from hydra.utils import get_method
>>> assert get_method("my_module.my_function") is my_module.my_function
"""
...

# Alias for get_method
get_static_method = get_method

def get_object(path: str) -> Any:
"""
Look up a callable based on a dotpath.

>>> import my_module
>>> from hydra.utils import get_object
>>> assert get_object("my_module.my_object") is my_module.my_object
"""
...