Iterating Over Registries
Sometimes, you want to iterate over all of the classes registered in a
ClassRegistry
. There are three methods included to help you do
this:
items()
iterates over the registry keys and corresponding classes as tuples.keys()
iterates over the registry keys.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:
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 register()
decorator to every class?
You can use the AutoRegister()
metaclass to automatically register
all non-abstract subclasses of a particular base class. See
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 SortedClassRegistry
:
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:
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 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
functools.cmp_to_key()
before providing it to the
SortedClassRegistry
initializer.
This is necessary because of how sorting works in Python. See Sorting HOW TO for more information.