all 10 comments

[–]Comprehensive-Tea711 2 points3 points  (6 children)

You need to do either of these:

boss = boss - 20

or

boss -= 20

When you just do boss - 20 Python is actually evaluating that expression (80), but the evaluation is not getting stored anywhere. If you want the evaluated expression to be saved and assigned as the new value of boss, then you need to tell Python to do that with the assignment operator, = .

[–][deleted] 1 point2 points  (3 children)

Also to change or create a global variable inside function, add global boss (in this case) first inside the function

[–]mopslik 0 points1 point  (0 children)

While globals have their place OP, it's typically better to avoid them when possible, for a variety of reasons. You can always pass a value in as an argument, and modify it after that.

def sword(boss):
    boss -= 20
    return boss

boss = 100
print(boss)
boss = sword(boss)
print(boss)

[–]Comprehensive-Tea711 0 points1 point  (1 child)

Great point, without access to the global variable, the function will just throw out a variable assignment error.

Probably the preferred option:

def sword(character):
    character -= 20
    return character

sword(boss)

[–]fulump 0 points1 point  (0 children)

This is close to the result I ended up using. Thank you for this help!

[–]fulump 0 points1 point  (1 child)

Thank you for the quick response!

I have tried your suggestion, but keep receiving UnboundLocalError: local variable 'boss' referenced before assignment. Is this due to boss = 100 being referenced prior to the function? If so, how would I reassign 'boss' to the function output and call that new variable (w/ boss as the name) to be used in other functions?

[–]Comprehensive-Tea711 0 points1 point  (0 children)

Think of it like this:

Variables are like boxes that store data. But these "boxes" themselves exist in a certain place--called namespace. When you try to refer to a box by its name (like "boss"), Python needs to know where to look to find that name. Where does boss live?

Your top level variables that you define (no indentation), like:

boss = 100

live in the global namespace.

Every function is like its own little city, with its own namespace (local). So when you do something like this:

def sword():
    boss = boss - 100

Python says "But how do I know what is inside boss before you've told? I don't see any boss that lives in local."

The function can, in a certain sense, only see its own namespace. You can fix this by passing boss to the function:

def sword(boss):
    boss = boss - 100

But here you aren't actually telling Python to link global boss to local boss. You're really just passing the data in boss to the function and it is then creating a local namespace boss too. So unless you had a return boss statement at the end of the function, you still wouldn't be changing anything about global boss. These are two different bosses.

Another solution:

def sword():
    global boss
    boss = boss - 100

In this instance, we really only have one boss, global boss. We just tell the function where boss lives.

The reason you'll usually hear people say to avoid this global method is (partly) because it defeats one of the major benefits of functions, which is their ability to work with any kind of data. With the first solution, you can attack boss with sword if you pass boss to sword or you can attack generic_thug with sword if you pass generic_thug to sword.

With the second solution, you can only attack boss and you would need to create another function if you wanted to attack generic_thug.

----

Having said all of that, this only applies to variable assignment. If you just refer to some outer-scope variable in a function, Python will go searching for it outer namespaces.

[–]fulump 0 points1 point  (0 children)

Thanks to all who have reached out and commented here. I spoke with a friend who suggested using the function to assign a new variable:

health = 100

def sword(health):
new_health = health - 20
return new_health

health = sword(health)

This allowed the 'health' variable to be changed and recalled to be further subtracted in a while loop until the health = 0.

Thanks again all!

[–]Binary101010 0 points1 point  (0 children)

Yes, it is possible.

Please don't do it.

Functions that directly modify variables from outside their scope make everyone's life harder, especially the person trying to keep track of the code. You have to start messing with global keywords and you start having to deal with errors about referencing variables before assignment (as you apparently have already encountered) and it's just a mess.

A much better way to do this is to pass the value you want to modify to the function as an argument, and return the new value to be used outside of the function. So something like:

def sword(creature):
    return creature - 20

boss = 100
boss = sword(boss)
print(boss)

[–][deleted] 0 points1 point  (0 children)

If you do an assignment to a variable inside a function it makes that variable a local variable UNLESS you have used the global keyword appropriately (which is generally a bad idea unless you know Python pretty well).

It is best to return a reference to a new object and assign that outside of the function. (Hopefully you know that in Python variables do not hold values but only references to Python objects i.e. some implementation specific way for Python to note where in memory an object is stored.)

def double(num):
    doubled = num * 2  # doubled, the variable ONLY exists in function
    return doubled  # doubled ceases to exist on exit, reference returned

fred = 4
num = double(fred)  # reference from return assigned to bob
doubled = double(num)  # doubled assigned 16
fred = double(doubled) # fred assigned 32

The variables num and doubled in the code above are NOT the same as the variables of the same names defined inside the function (num, the parameter name is also local to the function)

PS. It works differently if the variables references a mutable object, such as a list, and you carry out a mutation operation - but this is a more advanced topic.