During the process of making a simple football game, I have run across an issue I have not had before with class object initialisation, and can't find a good solution. As seen in the main method at the bottom of the code, iteratively creating new instances of Player() always share the same instance attribute values, despite these values being initialised within init. I know this must have to do with the use of the external methods as default values but I don't understand why these values would not be unique as they are not class attributes, they are instance attributes... right?
# import dependencies
import random
from position import Position
# external methods
def file_to_list(url: str) -> list[str]:
f = open(url, 'r')
all_lines = []
for line in f:
stripped_line = (line.strip())
all_lines.append(stripped_line)
f.close()
return all_lines
def get_random_name() -> str:
first_names: list[str] = file_to_list("assets/boys_names.txt")
last_names: list[str] = file_to_list("assets/last_names.txt")
return random.choice(first_names) + ' ' + random.choice(last_names)
def get_random_age() -> int:
return random.randint(17, 37)
def get_random_nationality() -> str:
dice: int = random.randint(1, 3)
if dice == 1:
return "British"
elif dice == 2:
return random.choice(
["French", "Spanish", "Italian", "Brazilian", "Portuguese", "Irish", "Danish", "Dutch", "German", "Belgian",
"Argentinian"])
else:
nationalities: list[str] = file_to_list("assets/nationalities.txt")
return random.choice(nationalities)
def get_random_score() -> float:
base: float = float(random.randint(1, 10))
if base == float(10):
return base
else:
decimal: float = random.uniform(0, 1)
return round(base + decimal, 1)
# class definition
class Player:
def __init__(self, name: str = get_random_name(), position: Position = None, age: int = get_random_age(),
nationality: str = get_random_nationality(), attack: int = get_random_score(),
defense: int = get_random_score()):
self.__name: str = name
self.__position: Position = position
self.__age: int = age
self.__nationality: str = nationality
self.__attack: float = attack
self.__defense: float = defense
self.__skill: int = int(((self.__attack + self.__defense) / 2) * 10)
@property
def name(self):
return self.__name
@property
def position(self):
return self.__position
@property
def age(self):
return self.__age
@property
def nationality(self):
return self.__nationality
@property
def attack(self):
return self.__attack
@property
def defense(self):
return self.__defense
@property
def skill(self):
return self.__skill
def __str__(self) -> str:
print("Name: " + self.__name)
if not self.__position:
print("Position not set.")
else:
print("Position: " + self.__position.name)
print("Age: " + str(self.__age))
print("Nationality: " + self.__nationality)
print("Skill: " + str(self.__skill))
print("Attack: " + str(self.__attack))
return "Defense: " + str(self.__defense)
if __name__ == "__main__":
player_list: list[Player] = []
for i in range(3):
new: Player = Player() #if i pass in a value it will not be shared.
player_list.append(new)
for p in player_list:
print(p)
Every time a player is created they share all the same values that are not passed in. Example output:
Name: Royal Vega
Position not set.
Age: 36
Nationality: British
Skill: 48
Attack: 1.2
Defense: 8.5
Name: Royal Vega
Position not set.
Age: 36
Nationality: British
Skill: 48
Attack: 1.2
Defense: 8.5
Name: Royal Vega
Position not set.
Age: 36
Nationality: British
Skill: 48
Attack: 1.2
Defense: 8.5
Instead of this, the expected output would return 3 unique objects of randomised age, name and skills as suggested in the code above.
"In Python function parameters can have default values. These default values are expressions which are executed when the function is defined, i.e. only once. The same default value will be used every time the function is called, thus modifying it will have an effect on every subsequent call. This can create some very confusing bugs."
You need to make the default values None and in the constructor add if statements such as:
if name == None:
self.__name = get_random_name()