Getting Started¶
As you saw in the introduction, you can create a new registry
using the class_registry.ClassRegistry class.
ClassRegistry defines a register method that you can use as a
decorator to add classes to the registry:
from class_registry import ClassRegistry
pokedex = ClassRegistry()
@pokedex.register('fire')
class Charizard(object):
pass
Once you’ve registered a class, you can then create a new instance using the corresponding registry key:
sparky = pokedex['fire']
assert isinstance(sparky, Charizard)
Note in the above example that sparky is an instance of Charizard.
If you try to access a registry key that has no classes registered, it will
raise a class_registry.RegistryKeyError:
from class_registry import RegistryKeyError
try:
tex = pokedex['spicy']
except RegistryKeyError:
pass
Registry Keys¶
By default, you have to provide the registry key whenever you register a new class. But, there’s an easier way to do it!
When you initialize your ClassRegistry, provide an attr_name
parameter. When you register new classes, your registry will automatically
extract the registry key using that attribute:
pokedex = ClassRegistry('element')
@pokedex.register
class Squirtle(object):
element = 'water'
beauregard = pokedex['water']
assert isinstance(beauregard, Squirtle)
Note in the above example that the registry automatically extracted the registry
key for the Squirtle class using its element attribute.
Collisions¶
What happens if two classes have the same registry key?
pokedex = ClassRegistry('element')
@pokedex.register
class Bulbasaur(object):
element = 'grass'
@pokedex.register
class Ivysaur(object):
element = 'grass'
janet = pokedex['grass']
assert isinstance(janet, Ivysaur)
As you can see, if two (or more) classes have the same registry key, whichever one is registered last will override any the other(s).
Note
It is not always easy to predict the order in which classes will be registered, especially when they are spread across different modules!
If you don’t want this behavior, you can pass unique=True to the
ClassRegistry initializer to raise an exception whenever a collision
occurs:
from class_registry import RegistryKeyError
pokedex = ClassRegistry('element', unique=True)
@pokedex.register
class Bulbasaur(object):
element = 'grass'
try:
@pokedex.register
class Ivysaur(object):
element = 'grass'
except RegistryKeyError:
pass
janet = pokedex['grass']
assert isinstance(janet, Bulbasaur)
Attempting to register Ivysaur with the same registry key as Bulbasaur
raised a RegistryKeyError, so it didn’t override Bulbasaur.
Init Params¶
Every time you access a registry key in a ClassRegistry, it creates
a new instance:
marlene = pokedex['grass']
charlene = pokedex['grass']
assert marlene is not charlene
Since you’re creating a new instance every time, you also have the option of
providing args and kwargs to the class initializer using the registry’s
get() method:
pokedex = ClassRegistry('element')
@pokedex.register
class Caterpie(object):
element = 'bug'
def __init__(self, level=1):
super(Caterpie, self).__init__()
self.level = level
timmy = pokedex.get('bug')
assert timmy.level == 1
tommy = pokedex.get('bug', 16)
assert tommy.level == 16
tammy = pokedex.get('bug', level=42)
assert tammy.level == 42
Any arguments that you provide to get() will be passed directly to the
corresponding class’ initializer.
Hint
You can create a registry that always returns the same instance per registry
key by wrapping it in a ClassRegistryInstanceCache. See
Factories vs. Registries for more information.