I have been working on a card-based combat game to practice Python coding, and I have objects for the players and the cards. I have been using functools.partial to assign an ability to each card object when I create it, and that has been working so far like this:
from functools import partial
class Player(object):
def __init__(self):
self.health = 100
def play_card(self, card):
card.ability(player=self)
human = Player()
computer = Player()
human.opponent = computer
computer.opponent = human
class Card(object):
def __init__(self, ability):
self.ability = ability
def attack(amount, player):
player.opponent.health -= amount
working_card = Card(partial(attack, 8))
human.play_card(working_card)
This is obviously a vastly simplified version of the code, but it's the important part. When I call human.play_card(working_card), the human Player object executes the function play_card(card=working_card), which then activates the working_card.ability() partial function, initiating the attack(amount=8, player=human) function.
But then I introduce a variable as one of the parameters of the partial function like this:
broken_card = Card(partial(attack, player.health))
human.play_card(broken_card)
The partial function is trying to determine the value of player.health when the Card object is first created, when it does not know what 'player' is. I want it instead to determine the value of player.health when attack(amount, player) is called, which is when 'player' has meaning. I have tried using lambda and placing the attack() function inside the Card object with no success, and I could not find any answers to this sort of question on the Internet. I need some way to work around partial trying to determine player.health when it is first called, if not explicitly than by a restructuring of the way my code's logic works.
You can use lambda
instead of partial
to defer the evaluation of parameters:
broken_card = Card(lambda *args, **kwargs: attack(player.health, *args, **kwargs))
Keep in mind that player
has to be available in the same scope as where this lambda
is defined. however. Since player
happens to be the second parameter of attack
, you can make lambda
make use of the known parameter:
broken_card = Card(lambda player: attack(player.health, player))