How to bring variables into functions - Python - python

I am having an issue where I want to bring a variable into a function by name.
I have tried this so far:
x = 5
def func(y = x):
return y ** 2
>>>print func()
25
>>>x = 4
>>>print func()
25
However if I try to edit that input variable after I input the code the number the function detects the old input so it would still print 25 as oppose to the new input squared.
Is there a cleaner way to pull a variable into a function that actually works?

Default functions values are calculated during function creation time, so even if you change the value of x it is not going to be changed in the function.
From docs:
Default parameter values are evaluated when the function definition is
executed. This means that the expression is evaluated once, when the
function is defined, and that the same “pre-computed” value is used
for each call.
Read: Using mutable objects as default value can lead to unexpected results.
If you want to access a global value then simply do:
def func():
return x**2
Or better pass it explicitly:
def func(y):
return y**2
Another reason why not to use global variables inside function:
>>> x = 10
>>> def func():
... print x
... x = 5
...
>>> func()
Traceback (most recent call last):
func()
File "<ipython-input-5-ab38e6cadf6b>", line 2, in func
print x
UnboundLocalError: local variable 'x' referenced before assignment
Using classes:
class A:
x = 5
#staticmethod
def func():
return A.x**2
...
>>> A.func()
25
>>> A.x = 10
>>> A.func()
100

Just reference the global variable directly, instead of using it as the default value of an argument.
def func():
return x ** 2

The default value of y is set during function definition, not when running it. So default value for y is set to value of x on def line.
This should work:
def func(y=None):
if y is None:
y = x
return y ** 2

Generally in python Keyword Arguments are always assigned a value and you cant change that assigned value . So in your code
x=5
def func(y = x):
return y ** 2
print func()
x=4
print func()
For the keyword argument y the value of x i.e 5 is assigned and we cant change that value. So even if you change the value of x you will get the same output as 25
To solve this the function might directly access the global variable and return the value like the below code
x=5
def func():
return x ** 2
print func()
x=4
print func()

This is the correct way to do this
x=5
def foo():
global x
return x**2
Or you can simply do it like this
x=5
def foo():
return x**2
In this case the same global variable is referenced if it exists.

Related

how to change an existing variable to a global variable inside of an if condition

I'm trying to make a little choose your own adventure game for fun and can't find a way to make this work. I want to be able to have a consistent pick up method where a specific action adds one to a variable and unlocks lines of dialogue under other actions. What I've got right now is something like this.
item = 0
while True:
act = input()
if "action" in act:
if(item != 1):
print("You don't have that item.")
continue
else:
print("You use the item.")
continue
if "take item" in act:
print("You take the item.")
global item
item = item + 1
continue
The issue I'm finding is that when I try to set the global variable, the program claims a syntax warning:
main.py:107: SyntaxWarning: name 'item' is assigned to before global declaration global item
since the variable is stated before the global, but if I don't state the variable before hand, the system I have in place to prevent the wrong dialogue from printing won't work. How do I have a conditional global variable that updates an existing local variable?
TLDR
You don't need the global keyword. Remove that line and your code will work.
Short Explanation
In Python, everything is "global" (not accurate, I will explain in long explanation) so you rarely need to use the global keyword. You can try the following code in the interactive Python REPL:
>>> def test(y):
... return x, y
...
>>> test(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in test
NameError: name 'x' is not defined
>>> x = 4
>>> test(3)
(4, 3)
Here, I'm using a function as an example, if and while/for loops are exactly the same.
I'm declaring a function called test that takes in a parameter y, but it will return the tuple (x, y). Here, x is undefined. You would expect that it returns an error, but it actually doesn't until you run it.
When we run test(3), Python will complain that x is not defined so it cannot run the function. However, if we define x = 4 even after our function declaration, the function will work. Hence test(3) will return (4, 3). Here, x is treated as a global variable.
Long Explanation
>>> x = 3
>>> y = 4
>>> def test2():
... x = 1
... y = 2
... return x, y
...
>>> test2()
(1, 2)
>>> x
3
>>> y
4
>>>
Notice that y is not touched, even though it's clearly being assigned a different value in the function test2.
How is this possible? Actually, Python functions technically do have a scope of their own, and changes to function variables are isolated and do not change the variables of the same name outside the function. Loops and if statements don't have their own scope, so the variables inside if statements are the exact variables outside them.
>>> x = 3
>>> y = 4
>>> if True:
... x = 1
... y = 2
...
>>> x
1
>>> y
2
When you reference a variable in functions, Python will check if a value is assigned to a variable of that name in that function (either as a parameter or inside the function block). If there is a variable with that name, Python will use the variable in the function (within the scope of the function). If not, Python will try to look for a function that's in the scope outside (either another function or the global scope). If Python searched till the global scope and there is no variable of that name, it will finally produce an error.
>>> x = 3
>>> y = 4
>>> def function1():
... x = 4 # this x is being used
... def function2():
... print(x)
... function2()
...
>>> function1()
4
>>> def test3():
... x = 1
... y = 2
... return (x, y)
...
>>> test3()
(1, 2)
>>> x
3
>>> y
4
This means, only when you are trying to change the value of a variable outside of the current scope, you will use the global keyword. However, this is BAD PRACTICE. Whenever you find yourself doing this, you need to think about whether you should refactor your code to using classes. It's a very bad practice to change the value of global variables inside functions no matter the language you are using.
>>> x = 3
>>> y = 4
>>> def test4():
... global x, y # refers to the global x and y
... x = 1
... y = 2
... return (x, y)
...
>>> test4()
(1, 2)
>>> x
1
>>> y
2

python: name resolution clarification?

I'm reading the python reference name resolution, which reads
A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
Based on that, I would expect the following code
x = 10
def f():
x = 5
class Test:
y = x
return Test
print(f().y)
to print 10, however it prints 5. is this a mistake in the reference, or am I misunderstanding something?
In this case, 'normal' rules apply:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # not local, normal rules apply
f()
# defined in f
In this second case, we would expect an UnboundLocalError: local variable 'x' referenced before assignment if we were inside a function:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # unbound local at this time
x = 'assigned in Test'
print(x)
But for the first print(x), x will be taken from the global namespace:
f()
# global
# assigned in Test
I think #khelwood gave the answer. The value in the variable:
Test.y
is an integer that you define, but you never give the x = 10 to the function.
Maybe what you want is actually:
x = 10
def f(x=5):
class Test:
y = x
return Test
print(f().y) #print default 5
print(f(x).y)
The last line print 10 since x is given to the function, and therefore the class set y as x
You can see the exception by wrapping f in another function that defines a local variable x:
x = 10
def g():
x = 7
def f():
class Test:
y = x
return Test
return f()
print(g().y)
Now the output is 7, as the lookup of x in the class statement bypasses the local scope of g to find the global value of x.
In your code, because class establishes a new namespace, but not a new scope, the value of x is taken from the current local scope, that of f.

Is this x in local scope of inner function?

Consider the following code, I expected the output to be 2 since the x to be printed is a local variable in the scope of inner function. However, it printed 2 instead. I don't understand why that is the case. Any explanation and/or comment is appreciated.
def outer():
x = 1
def inner():
x = 1
x += 1
print x
return x
There are two separate x variables here: the outer function has an x variable and the inner function has its own x variable.
From the moment there is an assignment to a variable somewhere in a function, the variable has a local scope.
So when you call outer(), you will return 1. The x in the inner function is a different one, and furthermore inner() is never called. As a result, the (local) x is not printed.
If you thus would have written:
def outer():
x = 3
def inner():
x = 1
x += 1
print x
return x
Then calling outer() will return 3. Even if you would have called inner() in the outer() function, it would not make any difference. Since the x in the inner() function is another one than the one in the outer() function (there is a local scope defined in inner). Although it would mean that you print 2, you will return 3.

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.

Assigning a function to a variable

Let's say I have a function
def x():
print(20)
Now I want to assign the function to a variable called y, so that if I use the y it calls the function x again. if i simply do the assignment y = x(), it returns None.
You simply don't call the function.
>>> def x():
>>> print(20)
>>> y = x
>>> y()
20
The brackets tell Python that you are calling the function, so when you put them there, it calls the function and assigns y the value returned by x (which in this case is None).
When you assign a function to a variable you don't use the () but simply the name of the function.
In your case given def x(): ..., and variable silly_var you would do something like this:
silly_var = x
and then you can call the function either with
x()
or
silly_var()
when you perform y=x() you are actually assigning y to the result of calling the function object x and the function has a return value of None. Function calls in python are performed using (). To assign x to y so you can call y just like you would x you assign the function object x to y like y=x and call the function using y()
The syntax
def x():
print(20)
is basically the same as x = lambda: print(20) (there are some differences under the hood, but for most pratical purposes, the results the same).
The syntax
def y(t):
return t**2
is basically the same as y= lambda t: t**2. When you define a function, you're creating a variable that has the function as its value. In the first example, you're setting x to be the function lambda: print(20). So x now refers to that function. x() is not the function, it's the call of the function. In python, functions are simply a type of variable, and can generally be used like any other variable. For example:
def power_function(power):
return lambda x : x**power
power_function(3)(2)
This returns 8. power_function is a function that returns a function as output. When it's called on 3, it returns a function that cubes the input, so when that function is called on the input 2, it returns 8. You could do cube = power_function(3), and now cube(2) would return 8.
lambda should be useful for this case.
For example,
create function y=x+1
y=lambda x:x+1
call the function
y(1)
then return 2.
I don't know what is the value/usefulness of renaming a function and call it with the new name. But using a string as function name, e.g. obtained from the command line, has some value/usefulness:
import sys
fun = eval(sys.argv[1])
fun()
In the present case, fun = x.
def x():
print(20)
return 10
y = x
y()
print(y)
gives the output
20
<function x at 0x7fc548cd3040>
so it does not actually assign the value returned by x() to the variable y.

Categories