File: iterating_over_registries.rst

package info (click to toggle)
python-phx-class-registry 4.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 260 kB
  • sloc: python: 886; makefile: 19
file content (160 lines) | stat: -rw-r--r-- 4,254 bytes parent folder | download
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