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')