Home > other >  How to simplify the iteration of class instances in python?
How to simplify the iteration of class instances in python?

Time:02-01

I have a Player class that stores a list of created instances in all. I would like to do so "P1" in Player instead of "P1" in Player.all

But the code below gives an error...

class Player():
    all = []
    def __init__(self, name):
        self.name = name
        Player.all.append(name)

    @classmethod
    def __iter__(cls):
        return cls.all
p1 = Player('P1')
'P1' in Player.all
'P1' in Player`

Traceback (most recent call last):
  at block 2, line 3
TypeError: argument of type 'type' is not iterable

How to simplify the iteration of class instances?

I try make @classmethod with __iter__(cls), but it is not work…

CodePudding user response:

You can make the class iterable, though I wouldn't recommend it. A class should not be responsible for maintaining a collection of its instances, only defining how those instances behave. Let whoever instantiates Player keep the list of instances it needs.


That said, you can define a custom metaclass that defines __iter__, since __iter__ has to be defined on the type of the thing you want to be iterable. Player is an instance of IterableClass, so IterableClass.__iter__ is the instance method that makes Player iterable.

class IterableClass(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.all = []

    def __iter__(self):
        yield from self.all


class Player(metaclass=IterableClass):
    def __init__(self, name):
        self.name = name
        # No *instance* attribute named all, so
        # use the *class* attribute defined by IterableClass.__init__
        self.all.append(name)

p = Player('p1')
assert list(Player) == ['p1']
assert 'p1' in Player

Instance methods defined by a metaclass and class methods defined in an instance of the metaclass appear similar on the surface, but are not the same. The former is still a regular function object, while the latter is an instance of classmethod; their __get__ methods (defined in accordance with the descriptor protocol) behave differently.

CodePudding user response:

Player cannot be both a class and a collection at the same time.

To get a more readable syntax for player existence check, you could hold the list of players in a separate (global) variable instead of a class member (e.g. Players) and you could then write the conditions as if 'P1' in Players.

Alternatively you could write a class method (e.g. Player.exists()) that checks xxx in Player.all and write your conditions as if Player.exists('P1')

  • Related