Python: Change a global variable, which comes from a function parameter - python

Is there any way to have a parameter of a function, which is a global variable and in the function itself you change the value of this variable. This way you have a global variable as a parameter of a function. In the function itself you change the value of this global variable. So for example:
x = 2
a = 5
def minus_one(x):
x -= 1 #take the global x
minus_one(x)
print(x) #Should print 1
minus_one(a)
print(a) #Should print 4

Yes, you can do something similar.
x = 2
def minus_one(var_name):
globals()[var_name] -= 1 # This accesses the global dict
minus_one('x') # Note that you must pass the variable name as string here
print(x) # Prints 1
Although, this is not recommended at all.
This can cause bugs that are extremely hard to find.
There is almost always a better way to do it
It's kind of ugly
In general, knowing the global dict exists is good. Knowing you should not use it is better.

Related

Can a function using a global variable use the variable's value from when the function was declared rather than when it was called?

Here is a very simplified example of what I am trying to do:
x = 3
def f():
print(x)
x = 5
f() #f prints 5 but I want it to print 3.
Is there a way, when declaring the function, to turn x into a constant that points somewhere other than the global variable x? I can't provide arguments to the function.
This is a pretty common trick (you usually see it in lambda expressions that want to bind a particular value within a loop):
x = 3
def f(x=x):
print(x)
x = 5
f() # prints 3
The trick is that default parameter values are evaluated at the time of function definition, so in the expression x=x, the x on the right hand side is evaluated (producing the value 3) and then stored as the default value of the x parameter in the function (which shadows the x in the outer scope).
You could equivalently write:
x = 3
def f(n=x):
print(n)
x = 5
f() # prints 3
which has the same result, but doesn't shadow the x variable.
From what I understand, you seem to want x to hold two values simultaneously - which is what complex data structures are for. A list would work fine, or a dict:
>>> x = [3]
>>> def f():
... print(x[0]) # always refers to first element. Functionally constant.
...
>>> x.append(5)
>>> f()
3
>>>
However, it sounds like you really have an XY problem, where you're asking about your solution instead of your actual problem. Go back to your code and check if this seems to be the case. If so, we might be able to point you towards a better way of solving your real issue.

Why is global keyword not ignored even if it is unreachable? (Python)

I am really new to programming and Python, so please really forgive me for my ignorance.
I just learned that using global can make a variable inside function be a global one.
However, I discovered something not with my expectation:
I tried the following code in Python 3.8 (forgive me for my ignorance as I don't know what else information I should provide):
>>> x = 0
>>>
>>> def function():
... if False:
... global x
... x = 1
...
>>> function()
>>> print(x)
and the result is 1.
However, I expected the code to have the same effect as the following code:
>>> x = 0
>>>
>>> def function():
... x = 1
...
>>> function()
>>> print(x)
which the result should be 0.
In my mind, the statement inside if False should not be executed, so it sounds strange to me.
Also, personally, I think that in some situation I would expect the variable inside a function, whether local or global, to be dependent on other codes... what I mean is, I would like to change if False to something like if A == 'A', while (I hope) I can control whether the x is global/local according to my conditional statement.
I tried to change if to while, but it's the same... there isn't a infinite loop, but the code global x is still executed/compiled...
I admit that it may sounds naive, and perhaps it just won't work in Python, but I really wonder why... It seems that the code global x is unreachable, but how come it is not ignored?
Can anyone please tell me about the reason? I would like to know more about the mechanism behind compilation(?)
Any help would be appreciated, thank you!
In python the global statement (and the nonlocal statement) are very different from the normal python code. Essentially no matter where a global statement in a function is, it influences always the current codeblock, and is never "executed". You should think more of it as a compiler directive instead of a command.
Note that the statement itself must come before any usage of the variable it modifies, i.e.
print(x)
global x
is a syntax error. The global statement can only modify variable behavior in the whole codeblock, you can't first have a non-global variable that later gets global and you can also not have conditional global variable
(I couldn't really find good documentation for this behavior, here it says "The global statement is a declaration which holds for the entire current code block." and "global is a directive to the parser. It applies only to code parsed at the same time as the global statement." but that doesn't seem super clear to me.)
There are more compiler directives in python, although they don't always look like one. One is the from __future__ import statements which look like module imports but change python behavior.
Global is not in execution path but in a scope. The scope is whole function. Statements like if for don't make scopes. If you use any assignment you create local variable. The same with global or nonlocal you bind symbol to variable from outside.
As Stanislas Morbieu typed, see doc.
Programmer’s note: global is a directive to the parser. It applies only to code parsed at the same time as the global statement.
Not at execution time.
x = 1
def fun():
y = x + 1
print(f'local x = {x}, y = {y}')
fun()
print(f'global x = {x}')
# Output:
# local x = 1, y = 2
# global x = 1
In example above, y uses global x (and adds 1).
x = 1
def fun():
y = x
x = y + 1
print(f'local x = {x}')
fun()
print(f'global x = {x}')
# Output:
# UnboundLocalError: local variable 'x' referenced before assignment
Look at last example. It doesn't assign y from global x because assignment in second line creates local x and y can not read local x before x assignment. The same with:
x = 1
def fun():
if False:
x += 1
fun()
# Output
# UnboundLocalError: local variable 'x' referenced before assignment
x assignment creates local variable.
If you want to change global variable under condition you can use globals().
x = 1
def set_x(do_set, value):
if do_set:
globals()['x'] = value
print(f'global x = {x} (init)')
set_x(False, 2)
print(f'global x = {x} (false)')
set_x(True, 3)
print(f'global x = {x} (true)')
# Output
# global x = 1 (init)
# global x = 1 (false)
# global x = 3 (true)
Proxy
I you want to decide with variable you want to use later (in the same scope) you need some kind of proxy IMO.
x = 1
def fun(use_global):
x = 2 # local
scope = globals() if use_global else locals()
scope['x'] += 1
print(f'local ({use_global}): x = {scope["x"]}')
print(f'global: x = {x} (init)')
fun(False)
print(f'global: x = {x} (false)')
fun(True)
print(f'global: x = {x} (true)')
# Output:
# global: x = 1 (init)
# local (False): x = 3
# global: x = 1 (false)
# local (True): x = 2
# global: x = 2 (true)
Maybe you can think about refactoring of your code if you need it.
If you can change local variable name (if not use globals() as above), you can proxy:
use dict (like in example above)
use list (x=[1]) and usage x[0]
use object (with builtin dict), example:
class X:
def __init__(self, x):
self.x = x
x = X(1)
def fun(use_global):
global x
my_x = x if use_global else X(2)
my_x.x += 1
print(f'local ({use_global}): x = {my_x.x}')
print(f'global: x = {x.x} (init)')
fun(False)
print(f'global: x = {x.x} (false)')
fun(True)
print(f'global: x = {x.x} (true)')
# Output:
# global: x = 1 (init)
# local (False): x = 3
# global: x = 1 (false)
# local (True): x = 2
# global: x = 2 (true)
Note. Variables in Python are only references. It is way you can not change x = 1 without global (or globals()). You change reference to local value 1.
But you can change z[0] or z['x'] or z.x. Because z referents to list or dict or object and you modify it content.
See: https://realpython.com/python-variables/#object-references
You can check real object by id() function, ex. print(id(x), id(my_x)).
As per the Python documentation, global is a directive to the parser so it is taken into account before the execution, therefore it does not matter if the code is reachable or not. The variable is global for the entire scope, which is the function in your case.

Why wont python return this variable, how can i get it to work?

When I run the following program it prints "2" and then "1" when i want it to print "2" and then "2". What have i done wrong?
def thingy(a):
a= a + 1
print(a)
return a
a=1
thingy(a)
print(a)
Many thanks if you can help.
You haven't re-assigned the a variable in your global scope. The a variable in the global scope isn't the same as in the function. To be more clear, look at this:
def thingy(arg):
arg = arg + 1
print(arg)
return arg
a=1 # Assign a to 1
thingy(a) # Do some work without altering variable
print(a) # Print a, which is 1...
a = thingy(a) # Here, a will be equals to two now.
With global option
You can also edit the global scope by adding global a at the beginning of the function and don't pass any argument to the function:
def thingy():
global a
a= a + 1
print(a)
return a
a=1
thingy()
print(a)
But prefer the first method, more cleaner.
You have remember that variables in functions are only in scope inside the function. So when you create this function:
def thingy(a):
a= a + 1
print(a)
return a
a is only in scope inside the function. No changes made to it exist outside the function. To use the returned value of a function you need to set a variable to be equal to the result of the function. So the a in the function and the a outside the function are not the same variable. Example:
def foo(a):
a = 2
return a
a = 1 #set a = 1
foo(a) #use a in function
a will still be one since the a inside the function is now out of scope.
a = 1
a = thingy(a)
print(a)
although it may look like you're setting a to 2 in the method, the scope is completely different. Imagine if you'd named the variable in the method b instead. They're different variables with the same name because of the scope.
Being a number your 'a' variable that you pass to thingy function is immutable and thus all manipulations inside your functions will not be passed back to original a. Though if you pass a as a list and change it in the function then you'll see all the changes you've made to the list after returning from a function.
def thingy(a):
a[0]= a[0] + 1
print(a[0])
a=[0]
thingy(a)
print(a[0])
you are mistaken with the a's.
functions live in their own world. they use vars of themselves. unless you tell them to use a global var.
you have lots of options to fix this:
use "global" to tell the function to use the global a.
def thingy(a):
global a;
a= a + 1
print(a)
return a
just by adding the line "global a" you tell the function "hey, you are not alone - use everyone's a".
the problem is that in your script, you use a as a global variable, and as the argument the function take. instead, write:
def thingy():
because you don't need the argument anymore.
so your code will look like this:
def thingy():
global a;
a= a + 1
print(a) #You don't need the "return a", you don't use it.
a=1
thingy()
print(a)
Use the return.
instead of using a global var, you can set "a" to whatever the function returns.
so instead of just calling thingy(a), use:
a = thingy(a)
I will use variable called "b" inside the function.
def thingy(b):
b = b + 1
print(b)
return b
a = 1
a = thingy(a)
print(a)

Python 3 syntax should work, but doesn't

It's so basic it should work. I want a function that adds a value to something
There must be something I don't know about python 3, so here we go.
x = 0
def foo(x=x): ##### with out x=x there is a error
x = x + 1 # option one
x = 1 # option two
# when we run it
foo()
print(x)
# it returns 0, it should return 1
x is a local variable in foo(); Assigning x as a default value for a keyword argument won't make it any less a local.
If you wanted it to be a global, mark it as such:
x = 0
def foo():
global x
x = x + 1
print(x)
foo()
print(x)
but you probably just wanted to pass in the value as an argument instead:
def foo(value):
return value + 1
x = 0
print(x)
x = foo(x)
print(x)
This is basically and example of scoping rules. The variable x within foo is local to foo, so nothing that happens to the local x changes anything outside foo, including the global x with which is actually a different variable. When the interpreter exits foo the global x comes back into scope and it hasn't changed from its initial value of 0. The function header foo(x=x) defines a local x whose default value is the global x. The interpreter allows it but it's generally considered bad programming practice to have the same variable name representing two variables because it leads to this kind of confusion.

Global Variables in functions with if statements

Okay, I am currently doing a project to make a blackjack game in python and I'm having some trouble. One of my issues is I dont know when to define a variable as global, specifically in functions with if statements. If I have a global variable outside the if statement, do I have to claim that the variable is global within the if statement as well? For example:
x = 5
def add():
global x <--- ?
x += 1
if x == 7:
global x <--- ?
x = 5
I'm pretty sure I need the "global x" at the 1st question mark, but what about at the second question mark? Would I still need to put a "global x" within my if statement if I wanted my if statement to update a global variable? Or does the global x at the beginning of the function make the x's inside the if statement global? Also, if I wanted to return x here, where should I do it?
Only one global statement is enough.
From docs:
The global statement is a declaration which holds for the entire
current code block.
x = 5
def add():
global x
x += 1
if x == 7:
x = 5
Also, if I wanted to return x here, where should I do it?
If you're using global in your function then the return x must come after the global x statement, if you didn't use any global statement and also didn't define any local variable x then you can return x anywhere in the function.
If you've defined a local variable x then return x must come after the definition.

Categories