简体   繁体   中英

Dynamically setting attribute as function in Python class

I am creating a simple game that contains classes called 'Player' and 'Strategy'. I want to assign a Strategy instance to the Player instance when the Player is created.

class Player(object):

    def __init__(self):
        self.Strategy = None

    def Decision(self, InputA, InputB):

        Result = self.Strategy(InputA, InputB)
        return Result

    def SetStrategy(self):
        # Sets a strategy instance to the Player instance


class Strategy(object):

    def Strategy1(self, InputA, InputB):
        return InputA * InputB

    def Strategy2(self, InputA, InputB):
        return (InputA - InputB) / 2

    def Strategy3(self, InputA, InputB):
        return 0

What I'm trying to achieve:

in[0] Player1 = Player()

in[1] Player2 = Player()

in[2]: Player1.SetStrategy('Strategy1')

in[3]: Player2.SetStrategy('Strategy3')

in[4]: Player1.Decision(2,5)

out[0]: 10

in[5]: Player2.Decision(3,6)

out[1]: 0

Searching here and via google shows me ways of doing it with monkey patching but the approach looks a little inelegant (and although I'm a beginner I think there's a better way to do it) - is there a way to do this with inheritance that I'm not seeing?

def strategy1(inputA, inputB):                  # 2
    return inputA * inputB

def strategy2(inputA, inputB):
    return (inputA - inputB) / 2

def strategy3(inputA, inputB):
    return 0

strategy = {
    'mul': strategy1,
    'diff': strategy2,
    'zero': strategy3
}

class Player(object):

    def __init__(self, strategy_name='mul'):      # 1
        self.strategy_name = strategy_name        # 5

    def decision(self, inputA, inputB):           # 4
        result = strategy[self.strategy_name](inputA, inputB)
        return result

player1 = Player()
player2 = Player()
player1.strategy_name = 'mul'                     # 3
player2.strategy_name = 'zero'
print(player1.decision(2, 5))
# 10

print(player2.decision(3, 6))
# 0

  1. Every player has a strategy, so don't allow instantiation of Player without assigning some strategy. You could use a default strategy (as shown below), or make strategy a mandatory argument.

  2. The strategies could be plain functions; I don't see a reason to bundle them as methods of a Strategy class. Always keep code as simple as possible; don't use a class when a function would suffice ; use a class when it provides some feature (such as inheritance) which makes the class-based solution simpler.

  3. In Python there is no need for getters/setters like setStrategy . You can use plain attributes for simple values, and properties to implement more complicated behavior. Attributes and properties use the same syntax, so you can switch from one to the other without having to change have the class is used.

  4. There is a convention (recommended in PEP8) that classes be named in CamelCase, and instances, functions and variables in lowercase. The convention is used ubiquitously, and following it will help other understand your code more easily.

  5. To make it easy to store the strategy in a database, you could store the strategy_name in the database, and use a lookup dict (such as strategy ) to associate the name with the actual function.

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