Registry¶
The Registry
can be used to register a class or a function.
Register an object¶
This section explains the basics on how to register an object (class or function). There are two approaches to register an object to the registry:
- by using the decorator
register
- by using the function
register_object
Decorator register
¶
You can use the decorator register
to register an object to the registry.
For example if you want to register the class ClassToRegister
, you can write:
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
@registry.register()
class ClassToRegister:
pass
print(registry.registered_names())
Output:
{'my_package.my_module.ClassToRegister'}
The class ClassToRegister
is registered with the name my_package.my_module.ClassToRegister
.
You can see the registered names by using the function registered_names
.
The function registered_names
returns the set of registered names.
You can also use the decorator register
to register a function:
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
@registry.register()
def function_to_register(*args, **kwargs):
pass
print(registry.registered_names())
Output:
{'my_package.my_module.function_to_register'}
The function function_to_register
is registered with the
name my_package.my_module.function_to_register
.
Function register_object
¶
The decorator approach works well when you can change the source code, but it will not work if you
cannot modify the source code.
You cannot use the decorator register
to register an object in a third party library.
To register a class or function from a third party library, you can use the
function register_object
.
For example if you want to register the class ClassToRegister
of the library third_party
, you
can write the following lines:
# file: my_package/my_module.py
from objectory import Registry
from third_party import ClassToRegister
registry = Registry()
registry.register_object(ClassToRegister)
Similarly to the decorator register
, you can use the function register_object
to register a
function:
# file: my_package/my_module.py
from objectory import Registry
from third_party import function_to_register
registry = Registry()
registry.register_object(function_to_register)
It is not possible to register a lambda function.
Please use a regular python function instead.
The registry will raise the exception IncorrectObjectFactoryError
if you try to register a lambda
function.
Advanced object registration¶
This section presents some advanced features to register an object.
Sub-registry¶
Previously we showed how to register an object to the registry, but all the objects will be
registered to the same registry which can be messy in some cases.
Instead, you can decide to use a hierarchical structure and to use sub-registries.
The registry will have a tree structure. registry
is the main registry, and you can add some
sub-registries.
Each sub-registry works like the main registry.
In fact, the main registry and the sub-registries are implemented with the same class (Registry
).
It is not a hard coded rule, but it is recommended to use one sub-registry for each "family" of
objects to group the similar objects.
If you want to register the class in the sub-registry other
, you can write:
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
@registry.other.register()
class ClassToRegister:
pass
print(registry.registered_names())
print(registry.other.registered_names())
Output:
{'other'}
{'my_package.my_module.ClassToRegister'}
You can check the registered objects in registry
and registry.other
.
The class ClassToRegister
is registered into registry.other
and not registry
.
Note that the sub-registry other
is registered to the main registry with the name other
.
It means you cannot use the name other
to register an object.
Each sub-registry is considered as a registered object in the parent registry.
The sub-registry other
will be created if it does not exist, so you do not have to manually
register the new sub-registry.
It is possible to have a nested structure where you add a sub-registry to a sub-registry:
from objectory import Registry
registry = Registry()
@registry.animal.cat.register()
class MyCat:
pass
@registry.animal.bear.register()
class MyBear:
pass
Register all the child classes¶
In some cases, you may want to register a class and all its child classes.
Instead of doing manually, you can use the function register_child_classes
.
This function will automatically register the given class and all its child classes.
It will find all the child classes and will add them to the registry.
It finds the child classes recursively, so it will also find the child classes of the child classes.
Let's imagine you have the following classes and you want to register them.
# file: my_package/my_module.py
from objectory import Registry
class Foo:
pass
class Bar(Foo):
pass
class Baz(Foo):
pass
class Bing(Bar):
pass
registry = Registry()
# If you want to register Foo and all its child classes.
registry.register_child_classes(Foo)
print(registry.registered_names())
# If you want to register Bar and all its child classes.
registry.bar.register_child_classes(Bar)
print(registry.bar.registered_names())
Output:
{'my_package.my_module.Foo', 'my_package.my_module.Baz',
'my_package.my_module.Bing', 'my_package.my_module.Bar'}
{'my_package.my_module.Bing', 'my_package.my_module.Bar'}
By default, the function register_child_classes
ignores all the abstract classes because you
cannot instantiate them.
If you want to also register the abstract classes, you can use the
argument ignore_abstract_class=False
.
The following example shows how to register all the torch.nn.Module
including the abstract
classes.
import torch
from objectory import Registry
registry = Registry()
registry.register_child_classes(torch.nn.Module, ignore_abstract_class=False)
Customized name¶
By default, the registry uses the full name of the object to identify the object in the registry.
It is possible to customize the name used to register an object.
For example if you want to register the class ClassToRegister
with the name my_class
, you can
write the following code:
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
@registry.register("my_class")
class ClassToRegister:
pass
registry.register_object(ClassToRegister, "my_class")
# or
registry.register_object(ClassToRegister, name="my_class")
One of the advantage of using customizable names is that you can define shorter name: my_class
is
shorter than my_package.my_module.ClassToRegister
.
But using customizable names is not a free lunch, and it has a cost: it will be your responsibility
to manage the names to avoid the conflicts.
If two objects are registered with the same name, only the last object will be registered.
For this reason, it is recommended to use the full name (default) because the full name is unique.
Please think carefully about the pros and cons of this feature before to use it. In most of the cases you probably do not need it. Keep in mind that with power comes responsibility.
Factory¶
The reserved keywords are:
_target_
: the class/function to instantiate_init_
: the constructor to use to instantiate a class
Remove objects¶
In the previous sections, we explain how to add objects to the registry. In this section, we will explain how to remove them.
Remove all the objects¶
If you want to remove all the objects in a registry, you can use the function clear
:
from objectory import Registry
registry = Registry()
class ClassToRegister:
pass
registry.register_object(ClassToRegister)
print(registry.registered_names())
registry.clear()
print(registry.registered_names())
Output:
set()
This function remove all the registered objects including the sub-registries.
For example, if the registry has a sub-registry other
, this line of code will also clear the
sub-registry other
.
You can use this function on a sub-registry to clear only this sub-registry.
For example, you can use the following command to remove all the classes and functions registered in
the sub-registry other
:
from objectory import Registry
registry = Registry()
registry.other.clear()
print(registry.other.registered_names())
Output:
set()
Note that by default, the function clear
removes the sub-registries from the current registry but
it does not clear the sub-registries.
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
class ClassToRegister:
pass
registry.other.register_object(ClassToRegister)
registry_other = registry.other
registry.clear()
print(registry.registered_names())
print(registry_other.registered_names())
Output:
set()
{'my_package.my_module.ClassToRegister'}
To also clear all the sub-registries, you can set nested=True
and the sub-registry other
is also
cleared.
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
class ClassToRegister:
pass
registry.other.register_object(ClassToRegister)
registry_other = registry.other
registry.clear(nested=True)
print(registry.registered_names())
print(registry_other.registered_names())
Output:
set()
set()
Remove a specific object¶
The function clear
is useful to remove all the objects in the registry, but it does not work if
you want to only remove a registered object and keep all the other registered objects.
# file: my_package/my_module.py
from objectory import Registry
registry = Registry()
@registry.register()
class ClassToRegister1:
pass
@registry.register()
class ClassToRegister2:
pass
print(registry.registered_names())
registry.unregister("my_package.my_module.ClassToRegister1")
print(registry.registered_names())
Output:
{'my_package.my_module.ClassToRegister1', 'my_package.my_module.ClassToRegister2'}
{'my_package.my_module.ClassToRegister2'}
If the object name is unique in the registry, you can only specify the object name:
from objectory import Registry
registry = Registry()
registry.unregister("ClassToRegister")
# is equivalent to
registry.unregister("my_package.my_module.ClassToRegister")
Please read the name resolution documentation to learn more about this feature.
Filters¶
Child class filter¶
Usually you want to control the objects that you want to add to the registry.
For example, it is usually a good idea to register similar objects in the same registry.
A usual scenario is to define a base class, and then register only the child classes of the base
class.
For example if you want to register torch.nn.Module
, you probably want to check that the object is
a torch.nn.Module
before to register it.
Let's imagine you want to register only torch.nn.Module
in the sub-registry other
.
import torch
from objectory import Registry
registry = Registry()
registry.set_class_filter(torch.nn.Module)
registry.register_object(torch.nn.Linear)
registry.register_object(torch.nn.Conv2d)
...
You can only register classes that inherits torch.nn.Module in the sub-registry other
.
If you try to register a class which is not a child class, the registry will raise the
exception IncorrectObjectFactoryError
:
import torch
from objectory import Registry
registry = Registry()
class Foo:
pass
registry.set_class_filter(torch.nn.Module)
registry.register_object(Foo)
The previous example will raise an exception because Foo
is not a child class
of torch.nn.Module
.
To unset the class filter, you can set it to None
:
from objectory import Registry
registry = Registry()
registry.set_class_filter(None)
Note that the class filter is only used when you try to register a new object. It will not filter the existing objects into the registry. You can look at the following example:
# file: my_package/my_module.py
import torch
from objectory import Registry
class Foo:
pass
registry = Registry()
registry.register_object(Foo)
print(registry.registered_names())
registry.set_class_filter(torch.nn.Module)
registry.register_object(torch.nn.Linear)
print(registry.registered_names())
Output:
{'my_package.my_module.Foo'}
{'my_package.my_module.Foo', 'torch.nn.Linear'}
You can see that the class my_package.my_module.Foo
is still registered after the filter is set
to torch.nn.Module
.
It is because this class was added before to set the class filter.
If you want to filter all the objects before to add them to the registry, please set the class
filter before to register the first object.
It will guarantee that all the registered objects are inherited from the filtered class.
Clear all filters¶
You can remove all the filters of a registry by using the function clear_filters
:
from objectory import Registry
registry = Registry()
registry.clear_filters()
By default, the function clear_filters
removes the filters on the current registry but not the
filters of the sub-registries.
To also remove the filters of all the sub-registries, you can set nested=True
:
from objectory import Registry
registry = Registry()
registry.clear_filters(nested=True)