Rayinator
Rayinator

Reputation: 45

Python: Altering the Value of Function Arguments

I'm currently making an RPG style game using the python tkinter libraries. I'm trying to work on battle system however I am running into issues with the damaging system

I'm trying to change the variables given into the function as arguments but of course I can't do that.. I've tried looking at other solutions and they simply wouldn't help due to the fact that tkinter works differently to other code.

Here is my code:

def Attack(EnemyHP,EnMax,GuiEnemyHP,EnemyHPBar,Width):
    Dmg = AtkDmg()
    EnemyHP = EnemyHP - Dmg
    GuiEnemyHP['text'] = Enemy + ": " + str(EnemyHP)+ '/' + str(EnMax)
    Loss = (Width / EnMax) * Dmg
    Width = EnemyBar_Width - Loss
    EnemyBar.place(x=110,y=0,width=Width,height=20)

Upvotes: 0

Views: 94

Answers (3)

Andris
Andris

Reputation: 973

As far as I know objects are passed over as references so you can modify them inside the function. As others pointed out, even though the pythonic way of passing an object as argument is not via reference, it makes it possible to alter the object's attributes from inside the function.

Another way to go around this limitation if you don't want to create objects is to pass over lists as they are mutable - you can replace an item of the list inside the function and then the caller will see the new modified list element instead of the old when the function returns. (Not a nice solution though.)

Upvotes: 0

Eric Duminil
Eric Duminil

Reputation: 54283

Your code will become a huge mess if you continue that way.

Python is an object-oriented language, so you probably should use classes and instances to describe the characters and interactions between them.

Here's a very basic implementation of Characters:

class Character:
    def __init__(self, name, hp_max):
        self.name = name
        self.xp = 0
        self.hp_max = hp_max
        self.hp = hp_max
        # TODO: define hp_bar here

    def is_dead(self):
        return self.hp <= 0

    def attack(self, opponent, damage):
        opponent.hp -= damage
        self.xp += damage

    def __str__(self):
        return '%s (%d/%d)' % (self.name, self.hp, self.hp_max)

hero = Character('Mario', 1000)
enemy = Character('Goomba', 100)

print(enemy)
# Goomba (100/100)

hero.attack(enemy, 50)
print(enemy)
# Goomba (50/100)

hero.attack(enemy, 50)

print(enemy)
# Goomba (0/100)
print(enemy.is_dead())
# True
print(hero.xp)
# 100

Upvotes: 2

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

As you already noticed, rebinding arguments within a function won't work here as you expected, since argument names are locals to the function. This leaves you with two solutions:

The functional way: return the new/updated values from your function:

def foo(arg1, arg2, argN):
    arg1 += 42
    arg2 = arg1 + argN
    argN -= arg2
    return arg1, arg2, argN

arg1, arg2, argN = foo(arg1, arg2, argN)

The OO way: pass a mutable object and mutate it:

class Arg(object):
    def __init__(self, arg1, arg2, argN):
        self.arg1 = arg1
        self.arg2 = arg2
        self.argN = argN


def foo(arg):
    arg.arg1 += 42
    arg.arg2 = arg.arg1 + arg.argN
    arg.argN -= arg.arg2


arg = Arg(x, y, z)
foo(arg)
# now arg.arg1, arg.arg2 and arg.argN have been updated
# - but NOT x, y and z which are untouched

Note that in this second example, a better design would be to have a method on Arg doing the effective update and either directly use this method or (if needed) call it from foo().

As a side note: I see quite a few unrelated things happening in your Attack function - at least one part which is domain related (computing damages and updating EnemyHP) and another part that is presentation related (updating GuiEnemyHP and EnemyBar). Those two parts would be better handled in two different layers of your app (resp. the model layer - which should not know anything about presentation - and the UI layer).

Upvotes: 0

Related Questions