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 klass_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 klass_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.

  ``a`` and ``b`` are tuples of (registry 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.