The problem isn't really that you can't pass things by references in Python. In fact, you always pass values by reference; Python never copies values unless you ask it to.
Or, more accurately, that whole C-based terminology is misleading in Python.
Anyway, when you do this:
James.attack(Keaton.statHEALTH)
This doesn't make a copy of the value in Keaton.statHEALTH
, it passes a reference to the exact same value. So, when attack
starts, your enemyhealth
variable is a name for that value.
And then you do this:
enemyhealth = (enemyhealth - self.statATK)
… the enemyhealth - self.statATK
returns a new value, and then you bind that new value to the enemyhealth
name. This has no effect on any other names for the old value.
There are two ways to solve this.
First, you don't actually need to mutate anything here. You're already doing return enemyhealth
at the end. That means the caller can get the new value that you calculated. So why not just use that?
Keaton.statHEALTH = James.attack(Keaton.statHEALTH)
And then everything works.
Of course in C++, you can mutate integer values. This seems a little silly when you think about it—turning the number 24 into the number 19 would break a lot of math, and probably make the universe stop working, and Python just isn't that powerful.
But you can easily build a type that can be sensibly mutated. In fact, you've already built one: a Fighter
holds an integer health value, but can be changed to hold a different one instead. So, you could write this:
def attack(self, enemy):
enemy.health = enemy.health - self.statATK
And then:
James.attack(Keaton)
Just as calling the old method with Keaton.health
made enemyhealth
into another reference to the same number, calling the new method with Keaton
makes enemy
into a reference to the same Fighter
. If you just reassigned a new value to enemy
, that wouldn't have any effect on the old value, the one that Keaton
refers to. But if you change the value in-place, obviously Keaton
is still referring to that now-changed value.