Are lists implicitly 'global' inside of functions? [duplicate] - python

This question already has answers here:
Modify global list inside a function
(5 answers)
Closed 6 years ago.
I was experimenting with this code:
def a():
#global p
p.append(4);
d=9
p=[2,3];
d=8
a();
print p # ----> [2, 3, 4]
print d # ----> 8
The variable d value is not changed as I didn't use the global keyword. But the list p was modified in function even though I didn't use global. Are all lists global by default in functions?

The critical difference is the assignment here. You are fine calling methods on existing global objects, but you can't assign to them without calling them global. In your code, the name d is being reassigned to reference another value. If you changed p with assignment you'd have a similar result
def a():
p = [5, 7] # new local variable, doesn't change global
p.append(9) # doesn't change global p
This makes sense if you think about what happens when python encounters the name for the first time. In the function you've provided, python will see p.append and say "hm, I don't have a local by the name p, let me look in the enclosing scope." It sees the global p and uses that.
In the example I've shown, python will say "there's no explicit global so I assume this is supposed to be a new local variable." and create one.
Names in python are just references. If python followed the behavior you are expecting you'd need a global for every function you called, let me explain:
def a():
p.append(1) # I should need 'global p' to do this
This would mean if you had
def g():
...
def f():
g() # this would also need 'global g', otherwise how does it see g?
def f2():
global g
def g(): # changes the global g function
return 0

Related

Nested Function Scope [duplicate]

This question already has answers here:
UnboundLocalError with nested function scopes [duplicate]
(3 answers)
Closed 2 years ago.
def foo():
a = 0
def bar():
a += 1
bar()
foo()
Why is it that this code does not execute when a is an immutable type, but does execute when it is mutable (a list, for example)?
I don't think the other answers narrate the entire story, so here's my 2 cents.The scope does not differ between types. That's not what's happening here. Rebinding a name, regardless of what that name refers to, will always cause an unbound local error to occur.
Some types in Python are mutable (once created, a value can be changed), and list is one of them. Some are immutable, and changing a value of these types requires creating a new object.
So, with the first example,
def foo():
a = 0
def bar():
a += 1
bar()
foo()
This doesn't work because you effectively have an assignment taking place here, a = a + 1. You can use non-local to make the above work, that being besides the point.
Doing the same thing with a list:
def foo():
a = []
def bar():
a.append(1)
bar()
foo()
This does indeed work. There is no assignment taking place here.
You can't re-bind the name to a different object, but if the object is mutable, you can modify its contents.
Now, there's 2 more cases you should be aware of.
def foo():
a = []
c = []
def bar():
a = c + [1]
bar()
print(a)
print(c)
foo()
This will work, however you should note that the a inside bar() now is local to bar, and the print() statement should reflect that.
But here's a gotcha
def foo():
a = []
def bar():
a = a + [1] #or even a += [1], doesn't matter
bar()
print(a)
foo()
This won't work! (And it's important you contrast this snippet with the first snippet, because that addresses why this has nothing to do with scopes. Take a minute to read it again.)
So this doesn't work and it's important to understand it.
If there is an assignment to a variable inside a function, that variable is considered local. Now, in the last case, when we did a = c + 1, it was more like a_local = c_nonlocal + 1.
In this case, a = a + 1 is a_local = a_local + 1 and hence, that will indeed cause an error. This is why, Rebinding a name, regardless of what that name refers to, will always cause an unbound local error to occur. In the case earlier(The third snippet), it wasn't rebinding it - it was creating a local variable. In the latter case(The fourth snippet), it was infact rebinding and hence the error.
There is a keyword nonlocal which is used to access nonlocal variables.
The nonlocal keyword is used to work with variables inside nested functions, where the variable should not belong to the inner function. --w3schools.com
def foo():
a = 0
def bar():
nonlocal a
a += 1
bar()
foo()
Thank you
That is because you are not returning any value from either functions and the second a is only local to the function bar().
Try using the nonlocal keyword like this:
def foo():
a = 0
def bar():
nonlocal a
a += 1
return a
return bar()
print(foo())
Output
1

Can I get the value of a non-local variable without using the nonlocal statement? [duplicate]

This question already has an answer here:
Where is nonlocals()?
(1 answer)
Closed 5 months ago.
I have a local variable x = "local" which unfortunately shares its name with both a global and a non-local variable. Without changing any of the names, can I access all three values? For x = "global" there is globals(), but what about the non-local variable?
Minimal example which illustrates the issue:
x = "global"
def f(x="nonlocal"):
def g():
x = "local"
print(x) # same as locals()["x"]
print(globals()["x"])
# here I want to print the non-local x
return g
f()()
I don't get your context that you have to use same name.
Anyway, you can capture outer function's locals as nonlocal variable.
x = "global"
def f(x="nonlocal"):
nonlocals = locals()
def g():
x = "local"
print(x)
print(nonlocals['x'])
print(globals()["x"])
return g
f()()
output:
local
nonlocal
global
Though you couldn't do this with the code written exactly as given, you can use inspect to get non-local variables. Note the changes to the call and return. If the caller is the outer scope instead of global scope, the previous frame will be f.
import inspect
x = "global"
def f(x="nonlocal"):
def g():
x = "local"
print(x)
print(globals()["x"])
print(inspect.currentframe().f_back.f_locals["x"])
return g()
f()
Output
local
global
nonlocal
This might not help in this specific situation, it really depends on how much control you have over the contents of f. If you don't have control over that, you can also monkey-patch f. A lot depends on context.
Edit: I didn't notice that the question specifically asked for this without using nonlocal. Leaving this here in case others find it useful.
I question the rationale behind this, but in Python 3, you can use the nonlocal keyword to access the previous scope, store that before re-declaration, then get it later.
x = "global"
def f(x="nonlocal"):
def g():
nonlocal x
y = x
x = "local"
print(x) # same as locals()["x"]
print(globals()["x"])
print(y)
return g
f()()
Output
local
global
nonlocal

why is python function capable of mutating non-global list? [duplicate]

This question already has answers here:
Understanding Python's call-by-object style of passing function arguments [duplicate]
(3 answers)
Closed 6 years ago.
myList = [5, 2]
def foo():
myList[0] = 6
In the example above, myList is still mutated, though it is non-global and not passed in by parameter. This doesn't work however, if the variable is not a list.
The scope rules are roughly like this:
Local scope: you define the variable inside a function
Enclosing scope: your function is inside another function (or multiple layers of functions), and one of the higher (enclosing) functions declared the variable
Global scope: the global scope (module or file)
Built-in: Any built-in values native to Python
This is known as the LEGB rule.
In your case, it is because you are not assigning a value to the name myList inside of foo(). You are only assigning a value to the 0 index of an existing list. Therefore the global value of myList is the scope you are using.
myList is a global variable, though. It exists in the context of foo, where you access and modify one of its elements.
myList = [0, 5]
def foo():
myList[0] = 6
print('Before foo: {}'.format(myList))
foo()
print('After foo: {}'.format(myList))
Output
Before foo: [0, 5]
After foo: [6, 5]
This doesn't work however, if the variable is not a list.
I assume that you tried something similar to the following:
a = 0
def bar():
a = 6
print('Before bar: {}'.format(a))
bar()
print('After bar: {}'.format(a))
Output
Before bar: 0
After bar: 0
In this case, you don't observe any change in the global variable a because within bar you're assigning to a new local variable a which shadows the global variable of the same name. You set the local variable a to 6, which has no effect as the variable is discarded as soon as the function ends.
You can show that even simple integers declared in the global scope can be accessed within function scopes by running something like the following. In this case, we specify that the function should modify an existing global variable b rather than assigning to a new local one.
b = 0
def baz():
global b
b = 6
print('Before baz: {}'.format(b))
baz()
print('After baz: {}'.format(b))
Output
Before baz: 0
After baz: 6

nested function change variable in an outside function not working [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 6 months ago.
def some_func(a):
def access_a():
print(a)
access_a()
outputs the value of a. However, if I want to change a in the nested function like this:
def some_func(a):
def change_a():
a += 1
print(a)
change_a()
it raises UnboundLocalError exception.
I know a is a nonlocal variable, but why can I access it without declaring nonlocal a?
Python scoping rules 101:
a name bound in a function body is considered local unless explicitely declared global (Python 2.x and 3.x) or nonlocal (Python 3.x only). This holds true whereever the assignment happens in the function's body. Trying to read a local variable before it's bound is of course an error.
if a name is read but not bound in a function's body, it will be looked up in enclosing scopes (outer function(s) if any then global scope). NB: functions arguments are de facto local names so they will never be looked up in enclosing scopes.
Note that a += 1 is mainly a shortcut for a = a + 1, so in your example a is local (bound in the function's body and not explicitely declared global or nonlocal), but you try to read it (the rhs of a = a+1) before it's bound.
In Python 3 you can solve this with a nonlocal statement:
>>> def outer(a):
... def change():
... nonlocal a
... a += 1
... print("before : {}".format(a))
... change()
... print ("after : {}".format(a))
...
>>> outer(42)
before : 42
after : 43
Python 2 doesn't have nonlocal so the canonical hack is to wrap the variable in a mutable container (typically a list but any mutable object would do):
>>> def outer(a):
... _a = [a]
... def change():
... _a[0] += 1
... print("before : {}".format(_a[0]))
... change()
... print ("after : {}".format(_a[0]))
...
>>> outer(42)
before : 42
after : 43
which is quite ugly to say the least.
Now while closures are quite handy, they are mostly the functional counterpart of objects : a way to share state between a set of functions while preserving encapsulation of this state, so if you find you have a need for a nonlocal variable perhaps a proper class might be a cleaner solution (though possibly not for your example that doesn't return the inner function but only uses it internally).
i have two solutions for you:
#first one:
# try with list, compound data types dict/list
def some_func(a):
def change_a():
a[0] += 1
print(a[0])
change_a()
some_func([1])
>>> 2
#second one
#reference pointer
from ctypes import *
def some_func_ctypes(a):
def change_a():
a[0] += 1
print a.contents, a[0]
change_a()
i = c_int(1)
pi = pointer(i)
some_func_ctypes(pi)
>>> c_int(2) 2
When you use the += operator, there is an assignment of a new value to a. This turns a to a local in the eyes of the interpreter.

Can you assign to a variable defined in a parent function? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Python nested functions variable scoping
After much trial and error I have eventually discovered that this doesn't work:
def a():
def b():
print x
x=2
x = 1
b()
print x
You get an exception (x not defined before being referenced). So it looks like b can read from x, but if it tries to assign to it, Python changes its interpretation of 'x' to be a local variable, which is now not defined.
Question for my own sick curiosity: is there any way of achieving this? Is there a way of explicitly accessing the scope of the parent function? (x is not global)
The nonlocal statement in Python 3 will do this.
Edit: In Python 2, there's not a simple way to do it. I suggest you use some mutable container object if you need this capability. For example:
def a():
def b():
print d["x"]
d["x"]=2
d = dict(x=1)
b()
print d["x"]
If you absolutely must emulate nonlocal for CPython 2, you can hack it with the Python C API this way:
import ctypes
import inspect
locals_to_fast = ctypes.pythonapi.PyFrame_LocalsToFast
locals_to_fast.restype = None
locals_to_fast.argtypes = [ctypes.py_object, ctypes.c_int]
def set_in_frame(frame, name, value):
frame.f_locals[name] = value
locals_to_fast(frame, 1)
def a():
def b(frame=inspect.currentframe()):
print x
set_in_frame(frame, "x", 2)
x = 1
b()
print x
You could also set the frame local, and instead of calling PyFrame_LocalsToFast(), you could manipulate the bytecode of a so that it uses LOAD_NAME instead of LOAD_FAST. Please don't do either of these things. There is surely a better solution for your use case.

Categories