Passing parameter to inner function - python

I have a function with inner function. I wonder what is a proper way to pass variables to inner function. From what I can see tables are passed by default, although I am not sure whether this is a undocumented workaround or python design.
def function():
def inner_function():
if a[0] > b[0]:
print("a[0] = {0}, b[0] = {1}".format(a[0], b[0]))
tmp = c
c = d
d = tmp
a = [4, 3]
b = [2, 1]
c = 1
d = 2
inner_function()
function()
python test.py output:
$ python test.py a[0] = 4, b[0] = 2 Traceback (most recent call last):
File "test.py", line 16, in
function()
File "test.py", line 14, in function
inner_function()
File "test.py", line 5, in inner_function
tmp = c
UnboundLocalError: local variable 'c' referenced before assignment
What is a proper way to pass variables from "function" to "inner_function"? Is there any other way than by parameter? Why there is an error on "c" variable reference and not on "a" table?

AFAIK c is throwing an error because is assigned inside inner_function so it is a different variable than the c variable defined in function. Variables a and b work because they are only read at inner_function so they are not being redefined. Renaming c and d to new_c and new_d make it works.
https://pyfiddle.io/fiddle/184e1778-adb7-4759-8951-da699751c31e/
More info about Python nested functions variable scoping
def function():
def inner_function():
if a[0] > b[0]:
print("a[0] = {0}, b[0] = {1}".format(a[0], b[0]))
tmp = c
new_c = d
new_d = tmp
a = [4, 3]
b = [2, 1]
c = 1
d = 2
inner_function()
function()

you need to state c and d variables as global (by the way this is not a good idea )
def function():
global c,d
a = [4, 3]
b = [2, 1]
c = 1
d = 2
def inner_function():
if a[0] > b[0]:
global c,d
print("a[0] = {0}, b[0] = {1}".format(a[0], b[0]))
tmp = c
c = d
d = tmp
inner_function()

Although your question has been answered I am not sure it is clear why your code produce this error.
Anyway to make it clear the line that causes problem is c = d although your interpreter disagrees.
Let me explain a bit. You are inside inner_function() and without these lines (c = d and d = tmp) you are referring to variables a, b, c and d assigned outside of inner_function(). So, implicitly you are referring to these variable (a, b, c and d that is) as global.
The moment you assign a value to a variable inside an inner function then the interpreter consider it to be local variable of this function. In this case c is considered local now (since there is an assignment inside inner_function() even after the statement the interpreter complains about tmp = c). So, normally the interpreter complains about a variable it has not been assigned a value but it's being accessed anyway.
So to make it clear, python does not distinguish between type of variable (as for example java does).
What is a proper way to pass variables from "function" to
"inner_function"?
Use parameters is the proper way.
Is there any other way than by parameter?
As mentioned by others you can use global variables but it's not recommended as approach.
Why there is an error on "c" variable reference and not on "a" table?
As I mentioned before there is no distinction between different types of variables. What make them different is that you are assigning in variable c a value but just accessing the value of a for example in the other case.
This specific answer (also mentioned by #raul.vila) provides a good explanation.
Finally, since it's not clear what you are trying to achieve here. There is a difference if you are trying to print a global (even implicit) variable in an inner function or you are trying to change the value of a global variable for inside an inner function.

I guess a Pythonic way definitely refers to a duck and a rabbit, possibly even a knight. I'd also second #Metareven on passing them in as arguments as Python has a very succinct way of handling them. This way you do not need to worry about #global variables. And you have a good idea about what goes in and as suggested what comes out.
def function(duck):
def inner_function(rabbit):
if rabbit[0][0] > rabbit[1][0]:
print("a[0] aka the first one behind the rabbit = {0}, \nb[0] aka the second one behind the rabbit = {1}".format(rabbit[0], rabbit[1]))
tmp = rabbit[2]
rabbit[2] = rabbit[3]
rabbit[3] = tmp
inner_function(duck)
#Let's sort out the arguments
a = [4, 3]
b = [2, 1]
c = 1
d = 2
function([a,b,c,d])
The function call returns the following:
python innner.py
a[0] aka the first one behind the rabbit = [4, 3],
b[0] aka the second one behind the rabbit = [2, 1]
Has this answered your question?

Related

the answer variable is not being updated in max depth of binary tree question [duplicate]

Consider this example:
def A():
b = 1
def B():
# I can access 'b' from here.
print(b)
# But can i modify 'b' here?
B()
A()
For the code in the B function, the variable b is in a non-global, enclosing (outer) scope. How can I modify b from within B? I get an UnboundLocalError if I try it directly, and using global does not fix the problem since b is not global.
Python implements lexical, not dynamic scope - like almost all modern languages. The techniques here will not allow access to the caller's variables - unless the caller also happens to be an enclosing function - because the caller is not in scope. For more on this problem, see How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?.
On Python 3, use the nonlocal keyword:
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.
def foo():
a = 1
def bar():
nonlocal a
a = 2
bar()
print(a) # Output: 2
On Python 2, use a mutable object (like a list, or dict) and mutate the value instead of reassigning a variable:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
Outputs:
[1, 1]
You can use an empty class to hold a temporary scope. It's like the mutable but a bit prettier.
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
This yields the following interactive output:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
I'm a little new to Python, but I've read a bit about this. I believe the best you're going to get is similar to the Java work-around, which is to wrap your outer variable in a list.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
Edit: I guess this was probably true before Python 3. Looks like nonlocal is your answer.
No you cannot, at least in this way.
Because the "set operation" will create a new name in the current scope, which covers the outer one.
I don't know if there is an attribute of a function that gives the __dict__ of the outer space of the function when this outer space isn't the global space == the module, which is the case when the function is a nested function, in Python 3.
But in Python 2, as far as I know, there isn't such an attribute.
So the only possibilities to do what you want is:
1) using a mutable object, as said by others
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
result
b before B() == 1
b == 10
b after B() == 10
.
Nota
The solution of Cédric Julien has a drawback:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
result
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
The global b after execution of A() has been modified and it may be not whished so
That's the case only if there is an object with identifier b in the global namespace
The short answer that will just work automagically
I created a python library for solving this specific problem. It is released under the unlisence so use it however you wish. You can install it with pip install seapie or check out the home page here https://github.com/hirsimaki-markus/SEAPIE
user#pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
outputs
2
the arguments have following meaning:
The first argument is execution scope. 0 would mean local B(), 1 means parent A() and 2 would mean grandparent <module> aka global
The second argument is a string or code object you want to execute in the given scope
You can also call it without arguments for interactive shell inside your program
The long answer
This is more complicated. Seapie works by editing the frames in call stack using CPython api. CPython is the de facto standard so most people don't have to worry about it.
The magic words you are probably most likely interesed in if you are reading this are the following:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
The latter will force updates to pass into local scope. local scopes are however optimized differently than global scope so intoducing new objects has some problems when you try to call them directly if they are not initialized in any way. I will copy few ways to circumvent these problems from the github page
Assingn, import and define your objects beforehand
Assingn placeholder to your objects beforehand
Reassign object to itself in main program to update symbol table: x = locals()["x"]
Use exec() in main program instead of directly calling to avoid optimization. Instead of calling x do: exec("x")
If you are feeling that using exec() is not something you want to go with you can
emulate the behaviour by updating the the true local dictionary (not the one returned by locals()). I will copy an example from https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Output:
hack!
I don't think you should want to do this. Functions that can alter things in their enclosing context are dangerous, as that context may be written without the knowledge of the function.
You could make it explicit, either by making B a public method and C a private method in a class (the best way probably); or by using a mutable type such as a list and passing it explicitly to C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
For anyone looking at this much later on a safer but heavier workaround is. Without a need to pass variables as parameters.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
You can, but you'll have to use the global statment (not a really good solution as always when using global variables, but it works):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()

Why are only mutable variables accessible in nested functions?

here is a simple (useless) function:
def f(x):
b = [x]
def g(a):
b[0] -= 1
return a - b[0]
return g
it works fine. let's change it a tiny bit:
def f(x):
b = x
def g(a):
b -= 1
return a - b
return g
Now it gives an error saying that b is undefined! Sure, it can be solved using nonlocal, but I'd like to know why does this happen in the first place? Why are mutables accessable and immutables aren't?
Technically, it has nothing to do with mutable or immutable (the language does not know whether a type is "mutable" or not). The difference here is because you are assigning to the variable in one case and just reading from it in the other case.
In your second example, the line b -= 1 is the same as b = b - 1. The fact you are assigning to the variable b makes a local variable named b, separate from the outside variable named b. Since the local variable b has not been assigned to when evaluating the right side of the assignment, reading from the local b there is an error.
In your first example, the line b[0] -= 1 is the same as b[0] = b[0] - 1. But that is not a variable assignment. That is just a list element access. It is just syntactic sugar for b.__setitem__(0, b.__getitem__(0) - 1). You are not assigning to the variable b here -- all you're doing is reading from the variable b two times (in order to call methods on the object it points to). Since you are only reading from b, it uses the external variable b.
If in the first example, with your mutable list, you did a variable assignment like you did in the second example, it would equally create a local variable, and the reading of that local variable before assignment would also not work:
def f(x):
b = [x]
def g(a):
b = [b[0] - 1]
return a - b[0]
return g

How to call on a variable which has been defined in a previous function?

My code is as follows...
def addition(a, b):
c = a + b
return c
And I then want to be able to use C later on in the program as a variable. For example...
d = c * 3
However, I get a NameError that 'C' is not defined... But I have returned c, so why can I not use it later on in the code?! So confused. Thanks!
(This is obviously a simpler version of what I want to do but thought I'd keep it simple so I can understand the basics of why I cannot call on this variable outside my function even though I am returning the variable. Thanks)
You have returned the value of c but not the whole variable i.e. the name c exists only within the scope it is instantiated.
So, if you want to use the value returned, you should re-assign it to a new name. You can do it by re-assigning it to c again, but it could be any name you wanted.
def addition(a, b):
c = a + b
return c
new_var = addition(1,2) #new_var gets the value 3
c = addition(2,3) #c gets the value 5
Take a look at this nice explanation about variables and scopes (link)
You usually define a function to use it later in your code. For that case, use another global variable c:
def addition(a, b):
c = a + b
return c
c = addition(1, 2)
d = c * 3 # d == 9
Functions allow this usage of repeated code, or procedure distinction, so that you can later write in your code
m = addition(4, 5)
and it will store the required result of the functionality into m.
If you want to define c in the function and use it later, you can use global variables.
c = 0
def addition(a, b):
global c
c = a + b
return c
It's not considered good to use globals, though. You could also call the function in the variable assignment.
d = addition(a, b) * 3
For this, you need to put real numbers in the place of a and b. I recommend you use the second option.

Python scoping in functions

Consider the following code:
>>> b = 3
>>> def change (b):
... b = 2
...
>>> change(b)
>>> print(b)
3
I am not confused by the fact that the print statement returns 3. Here's the question: is this because of scope, or because the argument that the function takes is not related to the outside b?
The b inside change is of a different scope than b outside of change. Inside the scope of the function, it does not matter what you call the variable before you pass it in - for now, it's going to be called b. As long as you don't return b from change and assign it to your "original" b, the first one won't change.
def change(b):
b = 2
return b
change(b)
print(b) # 3
b = change(b)
print(b) # 2
You are reassigning b. It does not change the parameter. You can test this using the id() function before and after the assignement.
The scope answers are partially correct for the object type you are passing.
Check out this link for information about how python "passes by value reference"
http://stupidpythonideas.blogspot.com/2013/11/does-python-pass-by-value-or-by.html
Here is another link that explains pythons Local -> Enclosed -> Global -> Built-in scope resolution with some examples:
http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/not_so_obvious_python_stuff.ipynb#python_legb
FWIW, the behavior is slightly different if we consider mutable objects:
>>> b = [3]
>>> def change(b):
... b[0] = 2
...
>>> change(b)
>>> print(b)
[2]
>>>
You have asked quite a tricky question, in the sense that the terminology you have used in framing the question is making people give you differing and weird answers.
You've given two choices:
we have a scope issue
b inside the function isn't related to b outside the function
Well, they are both true, and they are really just two ways to say the same thing.
Forgetting about terminology for a moment, realize that there is absolutely no difference between
b = 3
def f(a):
print(a)
f(b)
and
b = 3
def f(b):
print(b)
f(b)
The parameter names in a function's signature (the def line of a function) are local to the function, no matter what names are outside the function.
Now, you probably noticed that I sidestepped the issue of assignment within a function. That's because it's harder to explain quickly, but still be understandable and memorable to someone still in the fairly early stages of learning Python (which, no offense, it seems like you are).
Here is some reading that should help you understand fundamental concepts in Python:
http://nedbatchelder.com/text/names.html
http://www.jeffknupp.com/blog/2013/02/14/drastically-improve-your-python-understanding-pythons-execution-model/
You need to look at global variables. Just add global b at the beginning of your code and function, and it should work:
>>> global b
>>> b = 3
>>> def change():
... global b
... b = 2
...
>>> b
3
>>> change()
>>> b
2
>>>
You also need to remove the b parameter from def change(b), because that makes b both local and global. You can also return b:
>>> b = 3
>>> def change(b):
... b = 2
... return b
...
>>> b
3
>>> b = change(b)
>>> b
2
It is a scoping problem. b in the function is a local variable.
globals() is dictionary which hold all global variables.
b=3
def change (a):
globals()[a] = 2
print b
# 3
change("b")
print b
# 2

local variable referenced before assignment in python when i set it global

from random import randint
shifts = [4, 4.2, 5, 6, 7]
days_names = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday']
workers_names = ['Itai', 'Or', 'Reut', 'Kuka', 'Aviel']
counter = 1
def shift_arrange(worker):
for day in days.values():
counter+=1
global avilable_shifts
avilable_shifts = check_avilable_shifts(day)
if not random_shifte_selector(worker,day): soft_reset(worker)
I set counter as a global variable, and when i try to run this code i get the local variable error:
Traceback (most recent call last):
File "C:\Or\mypy\shift creator\shift cretor.py", line 144, in <module>
for w in workers.values(): shift_arrange(w)
File "C:\Or\mypy\shift creator\shift cretor.py", line 105, in shift_arrange
counter+=1
UnboundLocalError: local variable 'counter' referenced before assignmen
I saw some guy ask this question here, he deleted his pyc file or something(i don't know what is it) and its work fine. Why this is happen? Its not happen to other variables in the program.
Thanks, Or
You need to declare a global variable
def shift_arrange(worker):
global counter
for day in days.values():
counter+=1
...
Since you modify counter in that scope, python treats it as a local variable, unless you declare it as global. If you only need to read it, that isn't necessary.
Consider the following:
This works:
c = 0
def f():
print c
f()
While this does not:
c = 0
def f():
print c
c = 1
f()
While this does:
c = 0
def f():
global c
print c
c = 1
f()
print c # prints 1, f() modified the global value
There's a lot to look up here, but in general python has names, and things that are assigned to them. The name could be either in the local or global scope.
For reads, python looks through local scope, then through global scope, and uses the first one it finds (or error if it doesn't).
For writes... python needs to know where to put it. Normally, what it would do is look in the local scope, and if it's not there, create a variable there and assign the value. This would hide the global variable. You could have it also look in globals and use that one if it exists - but that could be undesirable & unexpected. So, you need a way to tell python to use a global variable instead if it exists (and then, it will create if it doesn't).
This leads to some odd behaviour sometimes as well. In addition to the earlier answer...
c = 0
# Passes. We are just looking up the global variable.
def f1(x):
return x + c
# Passes, but may not be as expected. Sets a local variable c to a value, does not
# modify the global one.
def f2(x):
c = x
# Correct way to do the above; now it sets the global variable.
def f3(x):
global c
c = x
# What if you mix them?
def f4(x):
c = c + x
# This fails. What happens is that first it sees that it's writing to c, and it's
# not marked global, so it assigns c to the local space. Then it tries to dereference
# it. Since we've marked it local, it masks the global one, and since it doesn't
# have a value, it throws an error. c += x works the same way and also fails.
# However, this works, though is just as equally wrong:
def f5(x):
d = c
c = d + x
# This one works, because we copy the value of the global c into the local d.
# Then, the second line assigns a local c to addition of local d and x.
# But does not update the global.
# Each line is separate though:
def f6(x):
d = c
c = c + 1
# You might think that d=c already made c local for the whole function. But it doesn't
# work like that. The first line just looks up the value for c, which it finds
# globally, but then forgets about it - it cares about the object c is the name of,
# not the name c itself. The second line still fails.

Categories