20

Given a class that keeps a registry of its Objects:

class Person(object):
   __registry = []

   def __init__(self, name):
       self.__registry.append(self)
       self.name = name

How would I make the following code work (without using Person.__registry):

for personobject in Person:
    print personobject

While researching I found a hint that one could go for a __metaclass__ with a __getitem__-method. Any ideas how this would look like?

Matthew Schinckel
  • 35,041
  • 6
  • 86
  • 121
Titusz
  • 1,435
  • 1
  • 22
  • 30

3 Answers3

33

You can make your class object iterable with a simple metaclass.

class IterRegistry(type):
    def __iter__(cls):
        return iter(cls._registry)

class Person(object):
    __metaclass__ = IterRegistry
    _registry = []

    def __init__(self, name):
        self._registry.append(self)
        self.name = name

(I have also changed __registry to _registry to make it easier to access from the metaclass). Then,

>>> p = Person('John')
>>> p2 = Person('Mary')
>>> for personobject in Person:
...     print personobject
...
<person.Person object at 0x70410>
<person.Person object at 0x70250>
dF.
  • 74,139
  • 30
  • 130
  • 136
  • Really nice tip: I didn't know this one :) – jkp Apr 11 '09 at 12:29
  • Why doesn't this work with just putting the def __iter__ in the Person class? Just wondering. – Paolo Bergantino Apr 11 '09 at 17:07
  • 2
    @Paolo Putting __iter__ in the Person class makes Person instances iterable, putting it in the metaclass makes the Person class itself iterable, which is what we want here. – dF. Apr 11 '09 at 19:26
  • 3
    Hi, I have tested this piece of code in Python 3.6.0 Anaconda 64-bit and it does not work. I fixed the print statement but you get a TypeError: 'type' object is not iterable. I am a newby in Python but I guess this is probably because the specifications for an iterable object have been changed in Python 3.6. – Athanassios Aug 23 '17 at 12:33
  • 1
    _registery doesn't get updated when you delete instances of the class though. – vrleboss Jan 25 '18 at 19:16
  • @Athanassios, for Python 3 see [Iterating over object instances](https://stackoverflow.com/a/32362984/2556118) or [Make class iterable](https://stackoverflow.com/questions/5434400/python-make-class-iterable). – Hans Ginzel Feb 12 '21 at 11:13
16

First, do not use double __ names. They're reserved for use by Python. If you want "private" use single _.

Second, keep this kind of thing as simple as possible. Don't waste a lot of time and energy on something complex. This is a simple problem, keep the code as simple as possible to get the job done.

class Person(object):
    _registry = []

    def __init__(self, name):
        self._registry.append(self)
        self.name = name

for p in Person._registry:
    print p
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    The point is that the _registry solution *is* the simplest possible way to do this. Anything else is just trying to "put lipstick on a pig". – S.Lott Apr 11 '09 at 12:37
4

you can do it with:

for item in Person.__registry:
    print(item)
SilentGhost
  • 307,395
  • 66
  • 306
  • 293