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 of 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, so you probably don’t want to rely on this behaviour!

If you want to prevent collisions, 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)

Because we passed unique=True to the ClassRegistry initialiser, 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 initialiser 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’ initialiser.

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.