0

I came across code which makes the class Person allow iteration here and here. It worked great for a single class, but I tried to modify it to allow iteration of multiple classes (separately) by changing _ClassRegistry to a dict, which would store

{class_str_name_1: [instance_obj_1, instance_obj_2, ...], ...}

instead of a list. My code is:

class ClassIter(type):
    def __iter__(cls):
        # iterate over the list in the dict value
        return iter(cls._ClassRegistry[cls.__name__])
    def __len__(cls):
        return len(cls._ClassRegistry[cls.__name__])         

class Person(metaclass=ClassIter):
    _ClassRegistry = {}
    
    def __init__(self, name, age):
        if type(name).__name__ in self._ClassRegistry.keys():
            self._ClassRegistry[type(self).__name__].append(self)
        else:
            self._ClassRegistry[type(self).__name__] = [self]

        self.name, self.age = name, age

class Dog(metaclass=ClassIter):
    _ClassRegistry = {}
    
    def __init__(self, name, age, owned_by):
        if type(name).__name__ in self._ClassRegistry.keys():
            self._ClassRegistry[type(self).__name__].append(self)
        else:
            self._ClassRegistry[type(self).__name__] = [self]
        
        self.name, self.age = name, age
        self.owned_by = type(owned_by).__name__

Alice = Person("Alice", 20)
Bob = Person("Bob", 25)

Spud = Dog("Spud", 6, Alice)
Buster = Dog("Buster", 12, Bob)

for p in Person:
    print(p.name, p.age)
    
for d in Dog:
    print(d.name, d.age, d.owned_by)

Unfortunately this did not work, and the output is

Bob 25
Buster 12 Person

while I was hoping it would produce

Alice 20
Bob 25
Spud 6 Alice
Buster 12 Bob

so not only is it using the class name Person instead of the name attribute (which is probably an easy fix but I've lost myself in the code) but also not storing the previous instances.

How can I either:

(1) convert this program to a class decorator (such as @iterator; class Person:) to make it easier to implement this for multiple classes? or

(2) fix the program to work as expected using the current implementation?

Any help much appreciated, I am unexperienced in OOP.

Nick_2440
  • 75
  • 1
  • 13
  • 1
    "It worked great for a single class, but I tried to modify it to allow iteration of multiple classes" Did you try using it unmodified? It looks to me like it's designed to work that way. Notice how the metaclass `__iter__` receives and uses a `cls` parameter? That represents the class which is using that metaclass, so it will be able to grab the class-specific instance list and use it. – Karl Knechtel Apr 16 '21 at 10:48
  • Which original code are you saying should work? My first or second link? – Nick_2440 Apr 16 '21 at 10:52
  • Either of the links, by my reading. There is really only one metaclass-based approach being demonstrated (and attempts at non-metaclass-based approaches, which fail). – Karl Knechtel Apr 16 '21 at 10:53
  • To my surprise, it (almost) worked as you said - thanks! The owned_by attribute still appears to be returning Person instead of Alice / Bob. – Nick_2440 Apr 16 '21 at 10:54
  • If you want a single dictionary that tracks instances of each class that uses the metaclass, then *that, too* should go in the metaclass. You can make it easier to use by giving the metaclass a `__new__` that updates the dict using the newly-created instance. – Karl Knechtel Apr 16 '21 at 10:54
  • The `Dog`'s `.owned_by` becomes `'Person'` because you set it using `type(owned_by).__name__`, i.e. you're explicitly looking at Alice and Bob's class (`Person`) and grabbing its built-in name attribute. Instances don't have such an attribute; of course, you've added an explicit `.name`, which is what you should be using. – Karl Knechtel Apr 16 '21 at 10:57
  • Thanks, this worked too. As a final comment, any idea how make my `ClassIter` into a decorator? – Nick_2440 Apr 16 '21 at 10:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/231206/discussion-between-karl-knechtel-and-nick-2440). – Karl Knechtel Apr 16 '21 at 10:59

0 Answers0