Getting Started¶
As you saw in the introduction, you can create a new registry
using the klass_registry.ClassRegistry
class.
ClassRegistry
defines a register
method that you can use as a
decorator to add classes to the registry:
from klass_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 klass_registry.RegistryKeyError
:
from klass_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 klass_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.