diff --git a/hydra/_internal/utils.py b/hydra/_internal/utils.py index 0af623a5085..71d8f88645d 100644 --- a/hydra/_internal/utils.py +++ b/hydra/_internal/utils.py @@ -579,7 +579,7 @@ def _get_kwargs( config: Union[DictConfig, ListConfig], **kwargs: Any, ) -> Any: - from hydra.utils import _call + from hydra.utils import instantiate assert OmegaConf.is_config(config) @@ -602,12 +602,12 @@ def _get_kwargs( if recursive: for k, v in final_kwargs.items(): if _is_target(v): - final_kwargs[k] = _call(v) + final_kwargs[k] = instantiate(v) elif OmegaConf.is_dict(v) and not OmegaConf.is_none(v): d = OmegaConf.create({}, flags={"allow_objects": True}) for key, value in v.items(): if _is_target(value): - d[key] = _call(value) + d[key] = instantiate(value) elif OmegaConf.is_config(value): d[key] = _get_kwargs(value) else: @@ -617,7 +617,7 @@ def _get_kwargs( lst = OmegaConf.create([], flags={"allow_objects": True}) for x in v: if _is_target(x): - lst.append(_call(x)) + lst.append(instantiate(x)) elif OmegaConf.is_config(x): lst.append(_get_kwargs(x)) else: diff --git a/hydra/utils.py b/hydra/utils.py index 1c9122e4d60..9309d79ea3d 100644 --- a/hydra/utils.py +++ b/hydra/utils.py @@ -15,18 +15,21 @@ log = logging.getLogger(__name__) -def call(config: Any, *args: Any, **kwargs: Any) -> Any: - return _call(config, *args, **kwargs) - - -def _call(config: Any, *args: Any, **kwargs: Any) -> Any: +def instantiate(config: Any, *args: Any, **kwargs: Any) -> Any: """ - :param config: An object describing what to call and what params to use. - _target_ : str : Mandatory target (class name, function name etc) - _recursive_: bool = True : recursive instantiation, defaults to True - :param args: optional positional parameters pass-through - :param kwargs: optional named parameters pass-through - :return: the return value from the specified class or method + :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) + _recursive_: Construct nested objects as well (bool). + True by default. + may be overridden via a _recursive_ key in + the kwargs + :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. + :return: if _target_ is a class name: the instantiated object + if _target_ is a callable: the return value of the call """ if OmegaConf.is_none(config): @@ -70,8 +73,8 @@ def _call(config: Any, *args: Any, **kwargs: Any) -> Any: raise type(e)(f"Error instantiating/calling '{cls}' : {e}") -# Alias for call -instantiate = call +# Alias for instantiate +call = instantiate def get_class(path: str) -> type: diff --git a/website/docs/patterns/instantiate_objects/config_files.md b/website/docs/patterns/instantiate_objects/config_files.md index 7db41729637..23ae40bf1c6 100644 --- a/website/docs/patterns/instantiate_objects/config_files.md +++ b/website/docs/patterns/instantiate_objects/config_files.md @@ -3,7 +3,7 @@ id: config_files title: Config files example sidebar_label: Config files example --- -[![Example application](https://img.shields.io/badge/-Example%20application-informational)](https://github.com/facebookresearch/hydra/tree/master/examples/instantiate/object) +[![Example applications](https://img.shields.io/badge/-Example%20applications-informational)](https://github.com/facebookresearch/hydra/tree/master/examples/instantiate) This example demonstrates the use of config files to instantiated objects. diff --git a/website/docs/patterns/instantiate_objects/overview.md b/website/docs/patterns/instantiate_objects/overview.md index 21542fd334e..f3320223c70 100644 --- a/website/docs/patterns/instantiate_objects/overview.md +++ b/website/docs/patterns/instantiate_objects/overview.md @@ -3,44 +3,55 @@ id: overview title: Instantiating objects with Hydra sidebar_label: Overview --- +[![Example applications](https://img.shields.io/badge/-Example%20applications-informational)](https://github.com/facebookresearch/hydra/tree/master/examples/instantiate) 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.call()` (and its alias `hydra.utils.instantiate()`) for instantiating objects and calling functions. Prefer `instantiate` for creating objects and `call` for invoking functions. +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: -- Class names : Call the `__init__` method -- Callables like functions, static functions, class methods and objects +- Constructing an object by calling the `__init__` method +- Calling functions, static functions, class methods and other callable global objects ```python -def call(config: Any, *args: Any, **kwargs: Any) -> Any: +def instantiate(config: Any, *args: Any, **kwargs: Any) -> Any: """ - :param config: An object describing what to call and what params to use. - Must have a _target_ field. - :param args: optional positional parameters pass-through - :param kwargs: optional named parameters pass-through - :return: the return value from the specified class or method + :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) + _recursive_: Construct nested objects as well (bool). + True by default. + may be overridden via a _recursive_ key in + the kwargs + :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. + :return: if _target_ is a class name: the instantiated object + if _target_ is a callable: the return value of the call """ ... -# Alias for call -instantiate = call +# Alias for instantiate +call = instantiate ``` 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. Any additional parameters are passed as keyword arguments to tha target. -For example, your application may have a User class that looks like this: +### Simple usage + +Your application may have a User class that looks like this: ```python title="user.py" class User: name: str - age : int + code : int - def __init__(self, name: str, age: int): + def __init__(self, name: str, code: int): self.name = name - self.age = age + self.code = code ```
@@ -51,7 +62,13 @@ class User: bond: _target_: user.User name: Bond - age: 7 + code: 7 + + + + + + ``` @@ -61,16 +78,71 @@ bond: ```python title="Instantiation" user : User = instantiate(cfg.bond) -assert isinstance(user, user.User) -assert user.name == "Bond" -assert user.age == 7 +# User(name="Bond", code=7) + +# Overriding the config on the callsite +user : User = instantiate(cfg.bond, + name="Batman") +# User(name="Batman", code=7) + +# None config -> None result +assert instantiate(None) is None ```
-For convenience, instantiate/call returns `None` when receiving `None` as input. -```python -assert instantiate(None) is None +### Recursive instantiation +Sometime it's useful to instantiate nested objects. Your app may have a class for a group of users: +```python title="group.py" +class Group: + name: str + users: List[User] + + def __init__(self, name: str, users: List[User]): + self.name = name + self.users = users +``` + + +
+ +
+ +```yaml title="Config" +group: + _target_: group.Group + name: Super heroes + users: + - _target_: user.User + name: Batman + code: 100 + - _target_: user.User + name: Wolverine + code: 666 + + ``` + +
+ +
+ +```python title="Instantiation" +group: Group = instantiate(cfg.group) +# Group( +# name="Super heroes", +# users=[ +# User(name="Batman", code=100), +# User(name="Wolverine", code=666) +# ] +# ) + + + + +``` + +
+
\ No newline at end of file diff --git a/website/docs/patterns/instantiate_objects/structured_config.md b/website/docs/patterns/instantiate_objects/structured_config.md index 31cc9b3609e..2a2db5b7599 100644 --- a/website/docs/patterns/instantiate_objects/structured_config.md +++ b/website/docs/patterns/instantiate_objects/structured_config.md @@ -4,7 +4,7 @@ title: Structured Configs example sidebar_label: Structured Configs example --- -[![Example application](https://img.shields.io/badge/-Example%20application-informational)](https://github.com/facebookresearch/hydra/tree/master/examples/instantiate/schema/my_app.py) +[![Example applications](https://img.shields.io/badge/-Example%20applications-informational)](https://github.com/facebookresearch/hydra/tree/master/examples/instantiate) This example demonstrates the use of Structured Configs to instantiated objects.