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
|
Iterating Over Registries
=========================
Sometimes, you want to iterate over all of the classes registered in a
:py:class:`ClassRegistry`. There are three methods included to help you do
this:
- :py:meth:`items` iterates over the registry keys and corresponding classes as
tuples.
- :py:meth:`keys` iterates over the registry keys.
- :py:meth:`values` iterates over the registered classes.
.. note::
Regardless of which version of Python you are using, all three of these
methods return generators.
Here's an example:
.. code-block:: python
from class_registry import ClassRegistry
pokedex = ClassRegistry('element')
@pokedex.register
class Geodude(object):
element = 'rock'
@pokedex.register
class Machop(object):
element = 'fighting'
@pokedex.register
class Bellsprout(object):
element = 'grass'
assert list(pokedex.items()) == \
[('rock', Geodude), ('fighting', Machop), ('grass', Bellsprout)]
assert list(pokedex.keys()) == ['rock', 'fighting', 'grass']
assert list(pokedex.values()) == [Geodude, Machop, Bellsprout]
.. tip::
Tired of having to add the :py:meth:`register` decorator to every class?
You can use the :py:func:`AutoRegister` metaclass to automatically register
all non-abstract subclasses of a particular base class. See
:doc:`advanced_topics` for more information.
Changing the Sort Order
-----------------------
As you probably noticed, these functions iterate over classes in the order that
they are registered.
If you'd like to customize this ordering, use :py:class:`SortedClassRegistry`:
.. code-block:: python
from class_registry import SortedClassRegistry
pokedex =\
SortedClassRegistry(attr_name='element', sort_key='weight')
@pokedex.register
class Geodude(object):
element = 'rock'
weight = 1000
@pokedex.register
class Machop(object):
element = 'fighting'
weight = 75
@pokedex.register
class Bellsprout(object):
element = 'grass'
weight = 15
assert list(pokedex.items()) == \
[('grass', Bellsprout), ('fighting', Machop), ('rock', Geodude)]
assert list(pokedex.keys()) == ['grass', 'fighting', 'rock']
assert list(pokedex.values()) == [Bellsprout, Machop, Geodude]
In the above example, the code iterates over registered classes in ascending
order by their ``weight`` attributes.
You can provide a sorting function instead, if you need more control over how
the items are sorted:
.. code-block:: python
from functools import cmp_to_key
def sorter(a, b):
"""
Sorts items by weight, using registry key as a tiebreaker.
:param a: Tuple of (key, class)
:param b: Tuple of (key, class)
"""
# Sort descending by weight first.
weight_cmp = (
(a[1].weight < b[1].weight)
- (a[1].weight > b[1].weight)
)
if weight_cmp != 0:
return weight_cmp
# Use registry key as a fallback.
return ((a[0] > b[0]) - (a[0] < b[0]))
pokedex =\
SortedClassRegistry(
attr_name = 'element',
# Note that we pass ``sorter`` through ``cmp_to_key`` first!
sort_key = cmp_to_key(sorter),
)
@pokedex.register
class Horsea(object):
element = 'water'
weight = 5
@pokedex.register
class Koffing(object):
element = 'poison'
weight = 20
@pokedex.register
class Voltorb(object):
element = 'electric'
weight = 5
assert list(pokedex.items()) == \
[('poison', Koffing), ('electric', Voltorb), ('water', Horsea)]
assert list(pokedex.keys()) == ['poison', 'electric', 'water']
assert list(pokedex.values()) == [Koffing, Voltorb, Horsea]
This time, the :py:class:`SortedClassRegistry` used our custom sorter function,
so that the classes were sorted descending by weight, with the registry key used
as a tiebreaker.
.. important::
Note that we had to pass the sorter function through
:py:func:`functools.cmp_to_key` before providing it to the
:py:class:`SortedClassRegistry` initializer.
This is necessary because of how sorting works in Python. See
`Sorting HOW TO`_ for more information.
.. _sorting how to: https://docs.python.org/3/howto/sorting.html#key-functions
|