Using arguments in Python functions - python

I'm aware of mutuable vs immutable arguments in Python, and which is which, but here is a weird issue I ran into with mutable arguments. The simplified version is as follows:
def fun1a(tmp):
tmp.append(3)
tmp.append(2)
tmp.append(1)
return True
def fun1(a):
b = fun1a(a)
print a #prints [3,2,1]
return b
def fun2a():
tmp = []
tmp.append(3)
tmp.append(2)
tmp.append(1)
return [True, tmp]
def fun2(a):
[b, a] = fun2a()
print a #prints [3,2,1]
return b
def main():
a=[]
if fun1(a):
print a #prints [3,2,1]
if fun2(b):
print b #prints garbage, e.g. (0,1)
As you can see the only difference is that fun2 points the passed in argument to reference a list created inside fun2a, while fun1 simply appends to the list created in main. In the end, fun1 returns the correct result, while fun2 returns random garbage rather than the result I'd expect. What's the problem here?
Thanks

This isn't so much of a mutable/immutable issue as one of scope.
"b" exists only in fun1 and fun2 bodies. It is not present in the main or global scope (at least intentionally)
--EDIT--
>>> def fun1(b):
... b = b + 1
... return b
...
>>> def fun2(a):
... b = 1
... return b
...
>>> fun1(5)
6
>>> fun2(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
(From my interpreter in terminal)
I'm guessing your 'b' was initialized somewhere else. What happened in the other function is of has no effect on this.
This is me running your exact code:
>>> main()
[3, 2, 1]
[3, 2, 1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in main
NameError: global name 'b' is not defined
>>> b = 'whatever'
>>> main()
[3, 2, 1]
[3, 2, 1]
[3, 2, 1]
whatever

As others have pointed out, there is no name 'b' in your main() function.
A better way of asserting how your code is behaving is to unit test it. Unit-testing is very easy in Python and a great habit to get into. When I first started writing Python a few years back the guy I paired with insisted on testing everything. Since that day I have continued and have never had to use the Python debugger as a result! I digress...
Consider:
import unittest
class Test(unittest.TestCase):
def test_fun1a_populates_tmp(self):
some_list = []
fun1a(tmp=some_list)
self.assertEquals([3, 2, 1], some_list)
def test_fun1a_returns_true(self):
some_list = []
ret = fun1a(tmp=some_list)
self.assertTrue(ret)
def test_fun1_populates_a(self):
some_list = []
fun1(a=some_list)
self.assertEquals([3, 2, 1], some_list)
def test_fun1_returns_true(self):
some_list = []
ret = fun1(a=some_list)
self.assertTrue(ret)
def test_fun2a_populates_returned_list(self):
ret = fun2a()
self.assertEquals([True, [3, 2, 1]], ret)
def test_fun2_returns_true(self):
some_list = []
ret = fun2(some_list)
self.assertTrue(ret)
def test_fun2_des_not_populate_passed_list(self):
some_list = []
fun2(some_list)
self.assertEqual(0, len(some_list))
if __name__ == '__main__':
unittest.main()
Each of these unit tests pass and document how your functions behave (save for the printing, you can add the tests for those if they are needed). They also provide a harness for when you edit your code, because they should continue to pass or start failing if you break something.
I haven't unit-tested main(), since it is clearly broken.

The problem may be related to the difference between lists and tuples. In fun2, don't put brackets around a,b.
In fun2a, return a tuple of the two objects and not a list. Python should write the varaibles correctly, if that's the problem that you're trying to solve.
Also, you called fun2 with argument b when b was never defined. Of course, the parameter for fun2 is never actually used, because it is rewritten before it is read.
In the end, your code should look like this:
def fun1a(tmp):
tmp.append(3)
tmp.append(2)
tmp.append(1)
return True
def fun1(a):
b = fun1a(a)
print a #prints [3,2,1]
return b
def fun2a():
tmp = []
tmp.append(3)
tmp.append(2)
tmp.append(1)
return (True, tmp)
def fun2():
b, a = fun2a()
print a #prints [3,2,1]
return b
def main():
a=[]
if fun1(a):
print a #prints [3,2,1]
if fun2():
print b #prints garbage, e.g. (0,1)
which should print [3,2,1] both times.

Related

A function returns another function. Why the function inside can scope a list from the parent but not a constant?

Consider this function:
def g():
x = []
def f():
x.append([0])
print(x)
pass
return f
Calling it:
test = g()
test()
I get the following output:
Out: [[0]]
We can reinitialize the test function and call it multiple times:
test = g()
for i in range(3):
test()
Resulting in the following output:
Out: [[0]]
[[0], [0]]
[[0], [0], [0]]
However, defining the following function:
def j():
x = 1
def f():
x += 1
print(x)
pass
return f
And calling it:
test = j()
test()
Results in an error:
UnboundLocalError: local variable 'x' referenced before assignment
The list seems to be in the inner function scope while the value is not. Why is this happening?
#rassar clearly explained the reason. Here I give a solution:
def j():
x = 1
def f():
nonlocal x
x += 1
print(x)
pass
return f
Actually this is not a simple question. Even though += looks like a bounded method call, an in-place operation (if you have experience in other languages). But what runs under is something like x = x.__add__(1) or x = x.__iadd__(1). So this is an assignment.
This is because j uses an assignment expression, where g uses a method call. Remember that x += 1 is equivalent to x = x + 1. If you change g to:
def g():
x = []
def f():
x += [[0]]
print(x)
pass
return f
You get:
>>> test = g()
>>> test()
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
test()
File "<pyshell#18>", line 5, in f
x += [[0]]
UnboundLocalError: local variable 'x' referenced before assignment
Because, the python complier knows that the variable 'x' in function f is a local variable not in the function j. So the error you mentioned above is occurred.
So you should use nonlocal. Please refer the link below.
Accessing parent function's variable in a child function called in the parent using python

Passing parameter to inner function

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?

Confused over variable necessity

What does the obj=lists[0] do in the following code?
lists = []
infile = open(path, 'rb')
while True:
try:
lists.append(pickle.load(infile))
except EOFError:
break
obj=lists[0]
while len(lists) > 3:
lists.pop(0)
print(lists)
infile.close()
I have tried to understand it but I cannot seem to see any reason for it.
Nothing.
obj is never referred to after its initial assignment, so it has no effect on anything.
The only possible way I could see that line doing anything, is if lists was some strange class whose __getitem__ call has a side effect. For example,
class StrangeList(list):
def __getitem__(self, idx):
self[idx] = 23
return 23
def a():
x = StrangeList([1,2,3])
print x
def b():
x = StrangeList([1,2,3])
obj = x[0]
print x
print "Calling a"
a()
print "Calling b"
b()
Result
Calling a
[1, 2, 3]
Calling b
[23, 2, 3]
Here, doing obj = x[0] does do something, even though obj is never used. But this is a contrived example; for your current code and for 99.9% of the classes you're likely to use in the future, __getitem__ won't behave this way.
Assuming this is all the code there is, there is no use for it and, as #Daniel Roseman mentioned, it must be a left over from some refactoring.
If there is more code in your program, I would suggest that the variable obj is being used to preserve the first value of the list. The loop below it consumes all elements in the list (except the last three), so if you are going to need the original first object in the list, you need to preserve it - hence, the obj attribution.

How to use the values assigned to variables during string formatting?

So this works:
>>> x = 1
>>> y = 2
>>> "a={a}, b={b}, a+b={c}".format( a=x, b=y, c=x+y )
'a=1, b=2, a+b=3'
But this doesn't:
>>> "a={a}, b={b}, a+b={c}".format( a=x, b=y, c=a+b )
NameError: name 'a' is not defined
Is there any way to make the second one work? (Say for example that x and y are function calls and I don't want to recompute them during string formatting)
The most pythonic (readable, in this case) solution for this is not to use a lambda function, but to cache a and b before the format() call:
a = function_x()
b = function_y()
"a={a}, b={b}, a+b={c}".format(a=a, b=b, c=a+b)
You'll be thankful when looking at the code 6 months from now.
You can do it with lambda:
def x():
return 1
def y():
return 2
>>> "a={a},b={b}, a+b={c}".format(**(lambda a=x(),b=y():{'a':a,'b':b,'c':a+b})())
'a=1,b=2, a+b=3'
this lambda expression is equal to calling predefined function:
def twosumm(a, b):
return {'a':a, 'b':b, 'c': a+b}
>>> "a={a},b={b}, a+b={c}".format(**twosumm(x(), y()))
'a=1,b=2, a+b=3'
Im also think that it is better to use simple and readable solution and just call x() and y() to get results before formatiing:
>>> a, b = x(), y()
>>> "a={a},b={b}, a+b={c}".format(a=a, b=b, c=a+b)
'a=1,b=2, a+b=3'
x = 1
y = 2
def f(x,y):
return (x,y,x+y)
print "a={}, b={}, a+b={}".format( *f(x,y) )
# or
print "a={0[0]}, b={0[1]}, a+b={0[2]}".format( f(x,y) )
.
EDIT
I think your question is wrongly written and that induces blurry understanding of it, and then wrong answers.
x and y are not function calls. As they appear, they are just identifiers
If you evoke function calls, I think it is because, in fact, you wish to obtain the result of something like that:
"a={a}, b={b}, a+b={c}".format( a=f(), b=g(), c=f()+g() )
but without having to write c=f()+g() because it implies that f() and g() are each executed two times.
Firstly, it will forever be impossible in Python to write something like .format( a=x, b=y, c=a+b ) or .format( a=f(), b=g(), c=a+b ) where a and b in c=a+b will refer to the same objects as a and b in a=x and b=y.
Because any identifier at the left side of = is in the local namespace of format() while any identifier at the right side of = is in the namespace outside of the function format().
By the way, that's why the identifiers at the left are called parameters and the identifiers at the right are the identifiers of objects passed as arguments.
Secondly, if you want to avoid writing f() two times (one time as an alone argument and one time in the expression f()+g()), and the same for g(), that means you want to write each only one time, as alone argument.
So , if I understand you well, you essentially wish to write something like that:
"a={a}, b={b}, a+b={}".format( a=f(), b=g() )
With current method str.format , this expression with three replacement fields { } is evidently not correct.
No matter, let's redefine the method format ! And then it's possible to pass only two arguments to format().
def fx(): return 101
def fy(): return 45
class Pat(str):
def __init__(self,s):
self = s
def format(self,x,y):
return str.format(self,x,y,x+y)
p = Pat("a={}, b={}, a+b={}")
print 'p==',p
print p.format(fx(),fy())
result
p : a={}, b={}, a+b={}
a=101, b=45, a+b=146
We can even do more complex things:
from sys import exit
import re
def fx(): return 333
def fy(): return 6
class Pat(str):
def __init__(self,s):
for x in re.findall('(?<=\{)[^}]+(?=\})',s):
if x not in ('A','M'):
mess = " The replacement field {%s] isn't recognised" % x
exit(mess)
self.orig = s
self.mod = re.sub('\{[^}]*\}','{}',s)
def modif(self,R):
it = iter(R)
return tuple(sum(R) if x=='{A}'
else reduce(lambda a,b: a*b, R) if x=='{M}'
else next(it)
for x in re.findall('(\{[^}]*\})',self))
def format(self,*args):
return ''.join(self.mod.format(*self.modif(args)))
print Pat("a={}, b={}, a+b={A}").format(fx(),fy())
print '******************************************'
print Pat("a={}, b={}, c={}, a+b+c={A}").format(fx(),fy(),5000)
print '******************************************'
print Pat("a={}, b={}, a*b={M}").format(fx(),fy())
print '******************************************'
print Pat("a={}, b={}, axb={X}").format(fx(),fy())
result
a=333, b=6, a+b=339
******************************************
a=333, b=6, c=5000, a+b+c=5339
******************************************
a=333, b=6, a*b=1998
******************************************
Traceback (most recent call last):
File "I:\potoh\ProvPy\Copie de nb.py", line 70, in <module>
print Pat("a={}, b={}, axb={X}").format(fx(),fy())
File "I:\potoh\ProvPy\Copie de nb.py", line 51, in __init__
exit(mess)
SystemExit: The replacement field {X] isn't recognised

Aliased name of a Function in Python

I want to find the name of the function as it was called ... i.e. the name of the variable that called the function. Using the basic recipes i.e. with __name__, func_name, or inspecting the basic stack does not work for me. For example
def somefunc():
print "My name is: %s" % inspect.stack()[1][3]
a = somefunc
a()
# would output: out: "My name is: somefunc"
# whereas I want it to output: "My name is: a"
My gut says I can do this, but I can't figure it out. Any python guru's out there?
It's probably impossible to do this 100% correctly, but you could give the following a try:
import inspect
import parser
# this flatten function is by mike c fletcher
def flatten(l, ltypes=(list, tuple)):
ltype = type(l)
l = list(l)
i = 0
while i < len(l):
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
i -= 1
break
else:
l[i:i + 1] = l[i]
i += 1
return ltype(l)
# function we're interested in
def a():
current_func = eval(inspect.stack()[0][3])
last_frame = inspect.stack()[1]
calling_code = last_frame[4][0]
syntax_tree = parser.expr(calling_code)
syntax_tree_tuple = parser.st2tuple(syntax_tree)
flat_syntax_tree_tuple = flatten(syntax_tree_tuple)
list_of_strings = filter(lambda s: type(s)==str,flat_syntax_tree_tuple)
list_of_valid_strings = []
for string in list_of_strings:
try:
st = parser.expr(string)
list_of_valid_strings.append(string)
except:
pass
list_of_candidates = filter(lambda s: eval(s)==current_func, list_of_valid_strings)
print list_of_candidates
# other function
def c():
pass
a()
b=a
a(),b(),c()
a(),c()
c(),b()
This will print:
['a']
['a', 'b']
['a', 'b']
['a']
['b']
It's pretty ugly and complicated, but might work for what you need. It works by finding all variables used in the line that called this function and comparing them to the current function.
The problem here is indirection. You could probably do something complicated like inspect the stack, get the code for the module that called the function, parse the line number from the stack to find the label used to call the function in the local context, and then use that, but that won't necessarily give you what you want anyway. Consider:
def func(x):
print get_label_function_called_with()
def func_wrapper(func_in_func_wrapper):
return func_in_func_wrapper
func_label = func
func_from_func_wrapper = func_wrapper(func_label)
func_from_func_wrapper()
Should this print func, func_in_func_wrapper, func_label, or func_from_func_wrapper? It might seem like an obvious answer at first, but given that you never really know what sort of indirection is going on inside code you are calling, you really can't know for sure.

Categories