简体   繁体   中英

Python how to pass class function from instance to another class

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. ie 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?

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:

  1. 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 .

  2. 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)

我也建议在 Character 类中初始化一个 power 值,或者在 Character 类中创建一个通用方法,然后在 Player 类中覆盖它。

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!")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM