Unusual problem with multiple return in Python - python

When I call the function "G()" which returns two values: p_T and q_T twice (see below) but using the same style, that's, P_T, neg = G(); U, Ign = G() and print sums of P_T and U, I get two different answers! Another thing is that both answers are incorrect!
I have only included part of the code which can simply aid in explaining the idea. The block of statements within the function G() under the for loop is executed N times as the loop suggests. Is there something being compromised by global variables declaration?
Any suggestion of what is wrong and how to fix it is appreciated!
def G():
global p_T; global q_T
for n in range(N):
p_T = U_T(EvolveU_T(p_T))
q_T = V_T(EvolveV_T(q_T))
return p_T, q_T
P_T, neg = G()
print sum(P_T)
U, Ign = G()
print sum(U)

You have global state. You mutate global state. Then you mutate it again, starting where you left off. So P_T is a result after N operations, and U is a result after 2N operations.
Don't use global state.

Because p_T and q_T are globals, their scope is not local to the function. So it's no surprise that you get two different answers after calling the function twice. Here's a simple example that demonstrates what's going on:
class C:
foo = ''
def __repr__(self):
return self.foo
x, y = C(), C()
def F():
global x
global y
x.foo += 'x'
y.foo += 'y'
return (x, y)
print F()
print F()
The globals x and y maintain their values because they are not scoped to the function. If they were declared and initialized inside the function without the global modifiers, you'd see (x, y) twice. But instead you see:
(x, y)
(xx, yy)
Generally globals are considered bad programming practice as they can lead to a confusing amount of state which is not localized to the function under consideration, as you've discovered.

Related

Are functions and helper functions treated differently in python?

Over here, the main function is g(x) and the helper function is h(). I noticed I can get the output for g(3), simply by binding x=3 and then doing any of the three
print(g(x))
g(x)
z=g(x)
But on the other hand, I noticed h() is outputted only when I type "print(h())". Are my observations correct or did I make a mistake? And also what is the logic behind this weird discrimination?
I like to think of it this way. Usually, if you had a line like "5" or "x=5", python doesn't give an output of 5. But functions have been given a special feature where they are invoked in any of the 3 ways. It's only that this special feature is being 'withdrawn' in the case of helper functions
The code you have written is equivalent to this:
def g(x):
def h():
any_name_you_want = 'abc'
return any_name_you_want
x += 1
print("in g(x)", x)
print(h())
return x
You cannot assign to a non-local variable inside a function. When Python creates the namespace for h, x is local to h because the assignment requires python to add the namespace for x at runtime.
Hence your code is also equivalent to :
def g(x):
def h():
return 'abc'
x += 1
print("in g(x)", x)
print(h())
return x
To get a sense of what is happening, run the following and then read up on UnboundLocalError:
def g(x):
def h():
x = x
return x
x += 1
print("in g(x)", x)
print(h())
return x

Closure and variable scope in python 3.x issue causing me confusion

I am using Python 3.7.0 and I am doing some experiments in order to comprehend the subtleties of variable scopes but I still don't understand this behaviour.
When I execute this code:
def f():
x=0
def g():y=x+1;x=y
return g
f()()
I get UnboundLocalError: local variable 'x' referenced before assignment
However when I execute this one:
def f():
x=0
def g():y=x+1
return g
f()()
It works fine. And even this one:
def f():
x=0
def g():x=1
return g
f()()
It also works fine. So I am confused. It seems to me that if assigning the value 1 to the nonlocal variable x in the g function works fine alone and on the other hand if assigning an expression containing x to a local variable y in the function g works also fine then the instructions y=x+1 and x=y should both work. I don't understand what is causing the error. I feel I am missing something fundamental in my understanding of Python's behaviour.
I'll take an ill-advised crack at this. In the following code:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1
x = y # assignment makes this x local to g()
return g
f()()
The assignment of x inside g() forces it to be a variable local to g(). So the first line causes the error as you accessing the value of an unassigned local. I believe this "assignment makes it local" logic doesn't follow the flow of your program -- the function is viewed as whole to make this determination and then the code is looked at line by line.
You can access any variable in scope from a function, but you can only set locals. Unless you've declared the variable you're assigning as global or nonlocal:
def f():
x = 0 # assignment makes this x local to f()
def g():
nonlocal x
y = x + 1
x = y # x is declared to come from a nonlocal scope
return g
f()()
In your second example, it's not an issue as you're only looking at the value of x, not setting it, so it can come from any appropriate scope:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1 # x comes from an outer scope
return g
f()()
Finally, your third example is the same as the first without the unssigned local variable usage error:
def f():
x = 0 # assignment makes this x local to f()
def g():
x = 1 # assignment makes this x local to g()
return g
f()()
It seems to me that if assigning the value 1 to the nonlocal variable
x in the g function works fine alone
How did you determine that the reassignment of x in f() worked fine?

function that contains a function of itself?

The title is a little weird, but I don't know exactly how this is called, so plz forgive me with the abstract title....
I've found a code like this online:
def lcs(xstr, ystr):
"""
>>> lcs('thisisatest', 'testing123testing')
'tsitest'
"""
if not xstr or not ystr:
return ""
x, xs, y, ys = xstr[0], xstr[1:], ystr[0], ystr[1:]
if x == y:
return x + lcs(xs, ys)
else:
return max(lcs(xstr, ys), lcs(xs, ystr), key=len)
I am new to python, and I don't understand how you can call lcs(xs, ys) in
return x + lcs(xs, ys)
To my understanding, lcs() is not yet fully defined, and I'm confused how you can call a function of itself inside itself....
Also, I don't know what key = len is doing in
max(lcs(xstr, ys), lcs(xs, ystr), key=len)
I know how max( 1st, 2nd ) works, but I don't know what the third parameter is doing. What does "key" mean, and why is "len" used as a value of "key"?
This is called recursion. The body of the function isn't evaluated at all until you call it. Think of lcs in the body as just a name; when you call the function, Python will apply the same lookup rules it applies to any name to see what it refers to; typically* it refers to the same function.
*Typically, because you can break a recursive function by playing some games with the names.
def foo():
print("hi")
foo()
g = foo
def foo():
print("new function")
If you call g(), it will output
hi
new function
instead of print an infinite stream of lines containing hi. (Well, almost infinite; eventually you'll get a RuntimeError because Python caps the size of the call stack.)

Accessing global function's variables in a local function

Here's my test script:
def main(): #global
n = 1
z = None
def addone(): #local
if not z:
n = n+1
addone()
print n
main()
I step into the addone() function once it hits the calling line.
At this point I can only see the variable z, but cannot see n.
Now, if n is referenced before assignment, shouldn't z be too?
Similarly, if I change n=n+1 to z='hi', I can no longer see z!
This is contrary to all my previous beliefs about local/global functions! The more you know, the more you know you don't know about Python.
Question(s):
Why can I see one but not the other?
Do I want to be prepending global to these variables I want to reassign?
The best solution is to upgrade to Python 3 and use in the inner function nonlocal n. The second-best, if you absolutely have to stick with Python 2:
def main(): #global
n = [1]
z = None
def addone(): #local
if not z:
n[0] += 1
addone()
print n[0]
main()
As usual, "there is no problem in computer science that cannot be solved with an extra level of indirection". By making n a list (and always using and assigning n[0]) you are in a sense introducing exactly that life-saving "extra level of indirection".
Okay, after some testing, I realised that it all has to do with the reassignment of variables.
for example:
def main(): #global
n = 1
z = None
def addone(): #local
if not z:
x = n+1
addone()
print n
main()
Now shows both n and z when I am inside the addone() function. This is because I am no longer trying to reassign n, makes sense to me so as to protect global variables from manipulation if one so happens to use similar names in local functions.

Is this a Python closure? (or, why does this code work?)

I am wondering why the following Python code works:
def sum(a, b):
return a+b+c
c=3
print sum(1,2)
# result: 6
I would have expected an compiling error.
How can the sum function already be defined when variable c is not (I would say that c is not in scope of the sum function) ?
Is this what they call a closure?
As I understood closures, the variable had to be already 'mentioned', like in the following code snippets (copied from this site). Here the x is already mentioned (although not 'filled in') above the definition of the inc() function.
def makeInc(x):
def inc(y):
# x is "closed" in the definition of inc
return y + x
In this case, c is found in the globals table. If you try to call sum(1,2) and c cannot be found in the function's scope, or it's parent scope (the global scope), then you will get a NameError exception.
Whilst this is similar to a closure, accessing global variables inside a function body is generally frowned upon, as it makes debugging much harder. A better example of a named closure in python (from Wikipedia):
def outer():
y = 0
def inner():
nonlocal y
y += 1
return y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
For more details, see the Wikipedia entry on Closures: Python example.
Python would throw an error if a variable is not defined when the function is called.
You call Sum when c is defined.
Python doesn't evaluate the body of sum until it is called. Since c is defined before sum is called, it is available to be used by sum.

Categories

Resources