When I'm trying to append()
the object itself into a list inside the object, it doesn't work, I don't understand why.
class PLayer:
CLASS_NAME = "player"
TOTAL_PLAYER_NUMBER = 0
TOTAL_PLAYER_LIST = []
PLAYER_ID_INCREMENT = 0
def __init__(self, name,
first_name,
birthday,
note,
player_id=None,
total_score=None,
tournament_score=None):
self.PLAYER_ID_INCREMENT += 1
self.TOTAL_PLAYER_NUMBER += 1
self.name = name
self.first_name = first_name
self.birthday = birthday
self.player_id = self.PLAYER_ID_INCREMENT
self.total_score = 0
self.tournament_score = 0
self.note = note
self.TOTAL_PLAYER_LIST.append(self.Player)
So the class Player have a list TOTAL_PLAYER_LIST = []
, then inside the __init__
I want to add the player that have been freshly created to the list with the last line self.TOTAL_PLAYER_LIST.append(self.Player)
but I get this error
line 25, in __init__
self.TOTAL_PLAYER_LIST.append(self.Player)
AttributeError: 'PLayer' object has no attribute 'Player'
I don't understand why. I've tried to put and remove self.
, the parenthesis also () for Player() .
.Player
attributeIf you do this, it works fine. Your class doesn't have any Player
attribute. The player you want to add is referenced in the self
variable, the instance.
self.TOTAL_PLAYER_LIST.append(self)
Let's see a simpler example with debug:
class Player:
TOTAL_PLAYER_NUMBER = 0
TOTAL_PLAYER_LIST = []
PLAYER_ID_INCREMENT = 0
def __init__(self, name):
self.PLAYER_ID_INCREMENT += 1
self.TOTAL_PLAYER_NUMBER += 1
self.name = name
self.player_id = self.PLAYER_ID_INCREMENT
self.TOTAL_PLAYER_LIST.append(self)
def __repr__(self):
return f"{self.name}:<{self.player_id}>"
print(Player("Doe")) # Doe:<1>
print(Player.TOTAL_PLAYER_LIST) # [Doe:<1>]
Name your class Player
, not PLayer
Class attribute vs Instance attribute
self.PLAYER_ID_INCREMENT += 1
This won't change the class attribute PLAYER_ID_INCREMENT
, just create an instance attribute of 1
:
doe = Player("Doe")
joe = Player("joe")
print(Player.TOTAL_PLAYER_NUMBER) # 0
print(doe.TOTAL_PLAYER_NUMBER) # 1
print(joe.TOTAL_PLAYER_NUMBER) # 1
Fix this with:
def __init__(self, name):
Player.PLAYER_ID_INCREMENT += 1
Player.TOTAL_PLAYER_NUMBER += 1
...
doe = Player("Doe")
joe = Player("joe")
print(Player.TOTAL_PLAYER_NUMBER) # 2
print(doe.TOTAL_PLAYER_NUMBER) # 2
print(joe.TOTAL_PLAYER_NUMBER) # 2
This happens because integers are immutable and += will "rebind" the incremented value to a new reference located... in self
, the instance namespace, not the class namespace.
A more pythonic way to do that is with metaclasses:
import typing
class CountInstances(type):
def __init__(cls, name, bases, dict_):
super().__init__(name, bases, dict_)
cls.count = 0
def __call__(cls, *args, **kwargs):
cls.count += 1
return super().__call__(*args, **kwargs)
class Player(metaclass=CountInstances):
total_player_list: typing.Final = []
def __init__(self, name):
self.name = name
self.player_id = self.count
self.total_player_list.append(self)
def __repr__(self):
return f"{self.name}:<{self.player_id}>"
doe = Player("Doe")
joe = Player("Joe")
print(Player.count) # 2
print(doe.count) # 2
print(joe.count) # 2
print(Player.total_player_list) # [Doe:<1>, Joe:<2>]
More information about metaclasses: What are metaclasses in Python?