create a list of an objectA inside the objectA
Question:
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() .
Answers:
Remove the .Player
attribute
If 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>]
Other critical issue with your code
-
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?
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() .
Remove the .Player
attribute
If 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>]
Other critical issue with your code
-
Name your class
Player
, notPLayer
-
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?