1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
Getting Started
===============
As you saw in the :doc:`introduction </index>`, you can create a new registry
using the :py:class:`class_registry.ClassRegistry` class.
:py:class:`ClassRegistry` defines a ``register`` method that you can use as a
decorator to add classes to the registry:
.. code-block:: python
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:
.. code-block:: python
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 :py:class:`class_registry.RegistryKeyError`:
.. code-block:: python
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 :py:class:`ClassRegistry`, provide an ``attr_name``
parameter. When you register new classes, your registry will automatically
extract the registry key using that attribute:
.. code-block:: python
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?
.. code-block:: python
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
:py:class:`ClassRegistry` initializer to raise an exception whenever a collision
occurs:
.. code-block:: python
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 :py:class:`ClassRegistry` initialiser,
attempting to register ``Ivysaur`` with the same registry key as ``Bulbasaur``
raised a :py:class:`RegistryKeyError`, so it didn't override ``Bulbasaur``.
Init Params
-----------
Every time you access a registry key in a :py:class:`ClassRegistry`, it creates
a new instance:
.. code-block:: python
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
:py:meth:`get` method:
.. code-block:: python
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 :py:meth:`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 :py:class:`ClassRegistryInstanceCache`. See
:doc:`factories_vs_registries` for more information.
|