Repository: Project-MONAI/MONAI

Issue:
Parse callables (functions, methods) similarly to objects
**Is your feature request related to a problem? Please describe.**
Support for functions/methods in config by making Monai call them instead of attempting to instantiate them.
```
calculate:
    _target_: "math.isclose"
    a: 0.001
    b: 0.002
```

**Describe the solution you'd like**
A solution like Hydra's (https://hydra.cc/docs/advanced/instantiate_objects/overview/) that can both instantiate or call would be perfect.

**Describe alternatives you've considered**
None

**Additional context**
While you could do `calculate: "$math.isclose(0.001, 0.002)"`, it does not allow argument overriding from CLI.


Discussion:
thanks for reporting, it's possible to use `@` to refer to an object, so that it supports overriding:

```py
from monai.bundle import ConfigParser

config = {"import statements": "$import math",
          "calculate": "$math.isclose(@a, @b)",
          "a": 0.001,
          "b": 0.005}
parser = ConfigParser(config)
print(parser.get_parsed_content("calculate"))  # False
parser["b"] = 0.001
print(parser.get_parsed_content("calculate"))  # True
```
Hi, yes, thank you, but you still have to refer the arguments manually in`"$math.isclose(@a, @b)"`. With `_target_` and instantiation that's not necessary. 

Do you think you're interested in adding support for calling functions/methods in the same fashion as object instantiation?


Patch:
diff --git a/monai/bundle/config_item.py b/monai/bundle/config_item.py
--- a/monai/bundle/config_item.py
+++ b/monai/bundle/config_item.py
@@ -236,7 +236,7 @@ def resolve_module_name(self):
         config = dict(self.get_config())
         target = config.get("_target_")
         if not isinstance(target, str):
-            raise ValueError("must provide a string for the `_target_` of component to instantiate.")
+            return target  # for feature discussed in project-monai/monai#5852
 
         module = self.locator.get_component_module_name(target)
         if module is None:
diff --git a/monai/utils/module.py b/monai/utils/module.py
--- a/monai/utils/module.py
+++ b/monai/utils/module.py
@@ -19,7 +19,7 @@
 from collections.abc import Callable, Collection, Hashable, Mapping
 from functools import partial, wraps
 from importlib import import_module
-from inspect import isclass, isfunction, ismethod
+from inspect import isclass
 from pkgutil import walk_packages
 from pydoc import locate
 from re import match
@@ -226,8 +226,7 @@ def instantiate(path: str, **kwargs):
             for `partial` function.
 
     """
-
-    component = locate(path)
+    component = locate(path) if isinstance(path, str) else path
     if component is None:
         raise ModuleNotFoundError(f"Cannot locate class or function path: '{path}'.")
     try:
@@ -241,8 +240,7 @@ def instantiate(path: str, **kwargs):
             pdb.set_trace()
         if isclass(component):
             return component(**kwargs)
-        # support regular function, static method and class method
-        if isfunction(component) or (ismethod(component) and isclass(getattr(component, "__self__", None))):
+        if callable(component):  # support regular function, static method and class method
             return partial(component, **kwargs)
     except Exception as e:
         raise RuntimeError(f"Failed to instantiate '{path}' with kwargs: {kwargs}") from e


Test patch:
diff --git a/tests/test_config_parser.py b/tests/test_config_parser.py
--- a/tests/test_config_parser.py
+++ b/tests/test_config_parser.py
@@ -248,6 +248,14 @@ def test_lambda_reference(self):
         result = trans(np.ones(64))
         self.assertTupleEqual(result.shape, (1, 8, 8))
 
+    def test_non_str_target(self):
+        configs = {
+            "fwd": {"_target_": "$@model().forward", "x": "$torch.rand(1, 3, 256, 256)"},
+            "model": {"_target_": "monai.networks.nets.resnet.resnet18", "pretrained": False, "spatial_dims": 2},
+        }
+        self.assertTrue(callable(ConfigParser(config=configs).fwd))
+        self.assertTupleEqual(tuple(ConfigParser(config=configs).fwd().shape), (1, 400))
+
     def test_error_instance(self):
         config = {"transform": {"_target_": "Compose", "transforms_wrong_key": []}}
         parser = ConfigParser(config=config)
@@ -274,6 +282,10 @@ def test_get_via_attributes(self):
         result = trans(np.ones(64))
         self.assertTupleEqual(result.shape, (1, 8, 8))
 
+    def test_builtin(self):
+        config = {"import statements": "$import math", "calc": {"_target_": "math.isclose", "a": 0.001, "b": 0.001}}
+        self.assertEqual(ConfigParser(config).calc(), True)
+
 
 if __name__ == "__main__":
     unittest.main()