Reputation: 357
I have a class of Player
with a number of attributes to measure their overall state. I have a second class of Character
(Class Type) that sets up some unique configurations for my players depending on their instance of character. Where I am having trouble, is calling a single generic function at the player level that measures the unique milestone goals for each character. i.e Check each player to see if they have reached their unique Milestone 1. I originally tried to do this with a series of conditional functions under the initialization, but this proved to be ill-advised in a class, so I moved it out to its own single function. Since doing this, it obviously fails because the attributes the function are checking for are in the parent class (Player
) and not the class where the function is defined (Character
).
class Player:
def __init__(self, player_num, name):
self.character = Character(name)
self.player_num = player_num
self.name = name
self.power = 0
self.position = 1
self.attributeLimit = 4
self.attributes = []
self.attachments = []
self.collection = []
self.visited = []
self.setCharacterValues()
self.ms1 = self.character.Milestone1
def setCharacterValues()
#Loads Character instance data into the Player instance.
class Character:
def __init__(self, name):
self.attributes = []
self.attachments = []
self.collection = []
self.visited = []
self.name = name
if name == 'char x':
self.attributes = [1,2,3]
#etc.
#etc.
def Milestone1(self):
if self.name == 'char x':
if self.power >= 20:
return True
else:
return False
if self.name == 'char y':
if self.position >= 5:
return True
else:
return False
#etc
for i in players:
if i.ms1():
print('Passed')
Now the only solution that I can think of, is to simply move the function from the Character
class to the Player
class. But this seems to be counter intuitive to me, because this function is measuring each unique characters ability to reach the milestone. It feels appropriate to leave it under Character, and I'm just missing an obvious step somewhere. Can anyone lend some assistance?
Upvotes: 1
Views: 804
Reputation: 20178
Please post the traceback error messages. I think the problem is in Milestone1()
you check self.power
and self.position
but self refers to the instance of Character
not Player
, and character has no power or position attributes.
Here are 2 fixes I can think of:
Inheritance: If you make player a subclass of character, then player would have all of the attributes of character and therefore be able to call ms1()
class Player(Character):
def __init__(self, player_num, name):
# self.character = Character(name) <-- not needed
# self.ms1 = self.character.Milestone1 <-- not needed
# since Player is a subclass of Character,
# use super to init & it already has name and Milestone1
super().__init__(name) # Py3 usage
# super(Player, self).__init__(name) # Py2 usage
I tested this out in a simple example on repl.it
and it works for me, try it by pressing play, output is 'n'
, then 't'
.
Note: in Python-2, the syntax for super
is different than Python-3, and you must also supply the subclass, Player
, and the instance, self
as args. I tested inheritance with super(Player, self).__init__(name)
in this sample repl.it using Python-2 and it also works.
Also Note: Python-2 classes should subclass object
to create new-style classes.
Composition:Fix Milestone1()
so it uses player as an argument, and therefore has access to power and position
class Character:
# no change
def Milestone1(self, player):
if self.name == 'char x':
if player.power >= 20:
return True
else:
return False
update Player
too, passing the player instance to Milestone1
class Player:
def __init__(self, player_num, name):
self.character = Character(name)
# self is the Player, lambda makes ms1 callable
self.ms1 = lambda: self.character.Milestone1(self)
Although then the name
attribute isn't really used, and this seems a bit hacky, but I tested it in this simple repl.it example and it also works.
In general, I think this is a good opportunity to use inheritance over composition. Ask yourself:
Is a player a character? (Inheritance: #1)
Does a player have a character? (Composition: #2)
Upvotes: 2
Reputation: 19259
What you have is a container class Player
that has a one-to-one relationship with the Character
. You do not want to duplicate the attributes of Character (like ms1
within Player
, especially not during __init__
before anything at all has happened in your game. Instead just access that Character's attributes from the Player instances whenever you need to check them:
player = Player(player_num=42, name='Ivan')
# do some stuff with `player` and `player.character` as the game progresses
# then somewhere deep in the game...
if player.character.ms1:
print("Congratulations on achieving your first milestone!")
Upvotes: 0
Reputation: 357
I'd recommend initizliaing a power value in the Character class as well, or creating a generic method in the Character class and then overriding it in the Player class.
Upvotes: 0