Python share global variable only for functions inside of function - python

I have a function which will recursively execute another function inside and I want to share variable for all execution of that function.
Something like that:
def testglobal():
x = 0
def incx():
global x
x += 2
incx()
return x
testglobal() # should return 2
However, I'm getting error NameError: name 'x' is not defined
There is hacky solution to make list and use first value of that list as x. But this is so ugly.
So how can I share x with incx function ? Or should I use completely different approach ?

This will work unless you are still using Python 2.x:
def testglobal():
x = 0
def incx():
nonlocal x
x += 2
incx()
return x
testglobal() # should return 2
Possible a cleaner solution though would be to define a class to store your state between method calls.

Use the nonlocal statement, so incx will use the x variable from testglobal:
def testglobal():
x = 0
def incx():
nonlocal x
x += 2
incx()
return x
testglobal()

You want to use the nonlocal statement to access x, which is not global but local to testglobal.
def testglobal():
x = 0
def incx():
nonlocal x
x += 2
incx()
return x
assert 2 == testglobal()
The closest you can come to doing this in Python 2 is to replace x with a mutable value, similar to the argument hack you mentioned in your question.
def testglobal():
x = [0]
def incx():
x[0] += 2
incx()
return x[0]
assert 2 == testglobal()
Here's an example using a function attribute instead of a list, an alternative that you might find more attractive.
def testglobal():
def incx():
incx.x += 2
incx.x = 0
incx()
return inc.x
assert 2 == testglobal()

Related

Confusion with scope in Python decorators

I'm trying to make a simple Python decorator, below is a simplified example of my use case.
def decorator(x):
def inner(f):
if x == 2:
x = 1
return f
return inner
#decorator(2)
def f():
pass
But I get this error
if x == 2:
UnboundLocalError: local variable 'x' referenced before assignment
Why is x not available? It's confusing because if I use print(x) instead of if x == 2 it works as expected.
Python can't determine xs actual scope, so before it runs, in the inner function it would raise the UnboundLocalError.
The way to fix it is modify the arguments of inner a bit, instead of:
def inner(f):
Make it to be:
def inner(f, x=x):
Then your code would work fine.
Or you could also use nonlocal, by adding nonlocal x in the beginning of the function inner.
def inner(f):
nonlocal x
if x == 2:
x = 1
return f

How do I find where the first call of the function was and return None accordingly?

I was given the following question:
Write a function last_in(x) that returns the value of x used in the previous call. The first time that the function is called, it will return None.
I did something that somewhat works, but something is missing. Here's my attempt:
def last_in(x):
def inner():
return x
return inner()
And the output of
print(last_in(3),last_in(4),last_in(5), last_in("a"),last_in("12"))
is
3 4 5 a 12
But the expected needs to be
None 3 4 5 a
What do I miss?
Thank you!
You can create an attribute for your function to store previous values, e.g. last_in.prev_val. This is somewhat similar to static variables in other languages. You just need to make sure you handle initializing the attribute appropriately. getattr is useful as #Feodoran mentions.
def last_in(x):
return_val = getattr(last_in, 'prev_val', None)
last_in.prev_val = x
return return_val
print(last_in(3), last_in(4), last_in(5), last_in("a"), last_in("12"))
Ouptput:
None 3 4 5 a
You are creating a function inner and immediately calling it. So this:
def last_in(x):
def inner():
return x
return inner()
is roughly equivalent to this:
def last_in(x):
return x
What you can do is, define a variable and access it using global keyword:
last_x = None
def last_in(x):
global last_x
return_value, last_x = last_x, x
return return_value
print(last_in(3), last_in(4), last_in(5), last_in("a"), last_in("12"))
None 3 4 5 a
Another approach would be to use a Mutable Default Argument:
def last_in(x, prev=[None]):
prev.append(x)
return prev[-2]
print(last_in(3),last_in(4),last_in(5), last_in("a"),last_in("12"))
This will produce:
None 3 4 5 a
You can maintain a global list of inputs passed to the function.
valueList=[]
def last_in(x):
def inner():
valueList.append(x)
if(len(valueList)!=1):
return valueList[len(valueList)-2]
else:
return None
return inner()
print(last_in(3),last_in(4),last_in(5), last_in("a"),last_in("12"))
OUTPUT:
None 3 4 5 a
Functions are objects and can have attributes. You can, therefore, store the previous number as an attribute.
def last_in(x):
if hasattr(last_in, 'last_num'): curr_num = last_in.last_num
else: curr_num = None
last_in.last_num = x
return curr_num
print(last_in(2))
print(last_in(3))
print(last_in(4))
Output:
None
2
3
You can do it without global variable:
def last_in(x, last = [None]):
v, last[0] = last[0], x
return v
print(last_in(3),last_in(4),last_in(5), last_in("a"),last_in("12"))
Prints:
None 3 4 5 a

Assign global variable to a function's local variable

I want to loop over some global variable i and each time compile a new function that uses the instantaneous value of i. I can then use each of these functions in future, independent of the current value of i.
The problem is I can't find a good way to make the global variable i stay local within the namespace of the function. For example:
i = 0
def f():
return i
i = 1
print(f()) #Prints 1, when I want 0
I found one solution but it seems very clumsy:
i = 0
def g(x):
def h():
return x
return h
f = g(i)
i = 1
print(f()) #Prints 0 as I wanted.
Is there a better way to do this, maybe some kind of decorator?
Python functions are objects, you can set an attribute on them:
i = 0
def f():
return f.i
f.i = i
i = 1
print(f()) # Prints 0 as you wanted
or:
for i in range(5):
f.i = i
print(f())
Note that an argument would do the same.

Variable modification triggers error [duplicate]

Where X is any programming language (C#, Javascript, Lisp, Perl, Ruby, Scheme, etc) which supports some flavour of closures.
Some limitations are mentioned in the Closures in Python (compared to Ruby's closures), but the article is old and many limitations do not exist in modern Python any more.
Seeing a code example for a concrete limitation would be great.
Related questions:
Can you explain closures (as they relate to Python)?
What is a ‘Closure’?
How does a javascript closure work ?
The most important limitation, currently, is that you cannot assign to an outer-scope variable. In other words, closures are read-only:
>>> def outer(x):
... def inner_reads():
... # Will return outer's 'x'.
... return x
... def inner_writes(y):
... # Will assign to a local 'x', not the outer 'x'
... x = y
... def inner_error(y):
... # Will produce an error: 'x' is local because of the assignment,
... # but we use it before it is assigned to.
... tmp = x
... x = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment
A name that gets assigned to in a local scope (a function) is always local, unless declared otherwise. While there is the 'global' declaration to declare a variable global even when it is assigned to, there is no such declaration for enclosed variables -- yet. In Python 3.0, there is (will be) the 'nonlocal' declaration that does just that.
You can work around this limitation in the mean time by using a mutable container type:
>>> def outer(x):
... x = [x]
... def inner_reads():
... # Will return outer's x's first (and only) element.
... return x[0]
... def inner_writes(y):
... # Will look up outer's x, then mutate it.
... x[0] = y
... def inner_error(y):
... # Will now work, because 'x' is not assigned to, just referenced.
... tmp = x[0]
... x[0] = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
10
>>> inner_error(15)
10
>>> inner_reads()
15
The only difficulty I've seen people encounter with Python's in particular is when they try to mix non-functional features like variable reassignment with closures, and are surprised when this doesn't work:
def outer ():
x = 1
def inner ():
print x
x = 2
return inner
outer () ()
Usually just pointing out that a function has its own local variables is enough to deter such silliness.
A limitation (or "limitation") of Python closures, comparing to Javascript closures, is that it cannot be used for effective data hiding
Javascript
var mksecretmaker = function(){
var secrets = [];
var mksecret = function() {
secrets.push(Math.random())
}
return mksecret
}
var secretmaker = mksecretmaker();
secretmaker(); secretmaker()
// privately generated secret number list
// is practically inaccessible
Python
import random
def mksecretmaker():
secrets = []
def mksecret():
secrets.append(random.random())
return mksecret
secretmaker = mksecretmaker()
secretmaker(); secretmaker()
# "secrets" are easily accessible,
# it's difficult to hide something in Python:
secretmaker.__closure__[0].cell_contents # -> e.g. [0.680752847190161, 0.9068475951742101]
Fixed in Python 3 via the nonlocal statement:
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.
#John Millikin
def outer():
x = 1 # local to `outer()`
def inner():
x = 2 # local to `inner()`
print(x)
x = 3
return x
def inner2():
nonlocal x
print(x) # local to `outer()`
x = 4 # change `x`, it is not local to `inner2()`
return x
x = 5 # local to `outer()`
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
comment for #Kevin Little's answer to include the code example
nonlocal does not solve completely this problem on python3.0:
x = 0 # global x
def outer():
x = 1 # local to `outer`
def inner():
global x
x = 2 # change global
print(x)
x = 3 # change global
return x
def inner2():
## nonlocal x # can't use `nonlocal` here
print(x) # prints global
## x = 4 # can't change `x` here
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 3 3
On the other hand:
x = 0
def outer():
x = 1 # local to `outer`
def inner():
## global x
x = 2
print(x) # local to `inner`
x = 3
return x
def inner2():
nonlocal x
print(x)
x = 4 # local to `outer`
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
it works on python3.1-3.3
The better workaround until 3.0 is to include the variable as a defaulted parameter in the enclosed function definition:
def f()
x = 5
def g(y, z, x=x):
x = x + 1

Python scope: how to reach local variable? [duplicate]

It's ok to get and print the outer function variable a
def outer():
a = 1
def inner():
print a
It's also ok to get the outer function array a and append something
def outer():
a = []
def inner():
a.append(1)
print a
However, it caused some trouble when I tried to increase the integer:
def outer():
a = 1
def inner():
a += 1 #or a = a + 1
print a
>> UnboundLocalError: local variable 'a' referenced before assignment
Why does this happen and how can I achieve my goal (increase the integer)?
In Python 3 you can do this with the nonlocal keyword. Do nonlocal a at the beginning of inner to mark a as nonlocal.
In Python 2 it is not possible.
Workaround for Python 2:
def outer():
a = [1]
def inner():
a[0] += 1
print a[0]
A generally cleaner way to do this would be:
def outer():
a = 1
def inner(b):
b += 1
return b
a = inner(a)
Python allows a lot, but non-local variables can be generally considered as "dirty" (without going into details here).

Categories