Can I iterate over a class in Python?

后端 未结 4 581
后悔当初
后悔当初 2020-12-29 03:21

I have a class that keeps track of its instances in a class variable, something like this:

class Foo:
    by_id = {}

    def __init__(self, id):
        sel         


        
相关标签:
4条回答
  • 2020-12-29 03:55

    You can create a class list and then call append in the init method as follows:

    class Planet:
      planets_list = []
    
      def __init__(self, name):
         self.name = name
         self.planets_list.append(self)
    

    Usage:

    p1 = Planet("earth")
    p2 = Planet("uranus")
    
    for i in Planet.planets_list:
        print(i.name)
    
    0 讨论(0)
  • 2020-12-29 04:07

    Try this:

    You can create a list with a global scope, define a list in the main module as follows:

    fooList = []
    

    Then add:

    class Foo:
      def __init__(self):
        fooList.append(self)
    

    to the init of the foo class

    Then everytime you create an instance of the Foo class it will be added to the fooList list.

    Now all you have to do is iterate through the array of objects like this

    for f in fooList:
        f.doSomething()
    
    0 讨论(0)
  • 2020-12-29 04:09

    If you want to iterate over the class, you have to define a metaclass which supports iteration.

    x.py:

    class it(type):
        def __iter__(self):
            # Wanna iterate over a class? Then ask that class for iterator.
            return self.classiter()
    
    class Foo:
        __metaclass__ = it # We need that meta class...
        by_id = {} # Store the stuff here...
    
        def __init__(self, id): # new isntance of class
            self.id = id # do we need that?
            self.by_id[id] = self # register istance
    
        @classmethod
        def classiter(cls): # iterate over class by giving all instances which have been instantiated
            return iter(cls.by_id.values())
    
    if __name__ == '__main__':
        a = Foo(123)
        print list(Foo)
        del a
        print list(Foo)
    

    As you can see in the end, deleting an instance will not have any effect on the object itself, because it stays in the by_id dict. You can cope with that using weakrefs when you

    import weakref
    

    and then do

    by_id = weakref.WeakValueDictionary()
    

    . This way the values will only kept as long as there is a "strong" reference keeping it, such as a in this case. After del a, there are only weak references pointing to the object, so they can be gc'ed.

    Due to the warning concerning WeakValueDictionary()s, I suggest to use the following:

    [...]
        self.by_id[id] = weakref.ref(self)
    [...]
    @classmethod
    def classiter(cls):
        # return all class instances which are still alive according to their weakref pointing to them
        return (i for i in (i() for i in cls.by_id.values()) if i is not None)
    

    Looks a bit complicated, but makes sure that you get the objects and not a weakref object.

    0 讨论(0)
  • 2020-12-29 04:22

    Magic methods are always looked up on the class, so adding __iter__ to the class won't make it iterable. However the class is an instance of its metaclass, so the metaclass is the correct place to define the __iter__ method.

    class FooMeta(type):
        def __iter__(self):
            return self.by_id.iteritems()
    
    class Foo:
        __metaclass__ = FooMeta
        ...
    
    0 讨论(0)
提交回复
热议问题