Python inner function sets vs nonlocal [duplicate] - python

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 1 year ago.
I believe I know the answer to this, but wanted to double-check because I find this a bit confusing.
def outerFunc():
mySet = set()
a = 0
def innerFunc():
mySet.add(1)
mySet.add(2)
a = 7
innerFunc()
print(mySet) # {1, 2}
print(a) # 0
Here, if I want to change the value of a, I need to use nonlocal. The fact that the set changes is just because sets are passed by reference? So, in an inner function we have access to the values of the outer function's variables, but cannot modify them unless they're references?

You can check the python document
In Python, variables that are only referenced inside a function are
implicitly global. If a variable is assigned a value anywhere within
the function’s body, it’s assumed to be a local unless explicitly
declared as global.
So if you assigned a variable and the variable without global just affects the local.
For example, if you assigned value to mySet, then it also does not change.
def outerFunc():
mySet = set()
def innerFunc():
mySet = {1}
mySet.add(2)
innerFunc()
print(mySet) # ''

Related

Why it's impossible to reassign global name within local scope (without `global` keyword) with using the global name's value in reassignment? [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 5 months ago.
If we run this code
a = 1
def foo():
b = a + 2
print(b)
foo()
it works.
But if we run this code
a = 1
def foo():
b = a + 2
print(b)
a = a + 4
print(a)
foo()
it doesn't work.
Question:
why in the first example b = a + 2 works without errors but in the second example the same line of code b = a + 2 is broken?
why if there is no reassignment of a we can get a from a global scope but if there is a reassignment we don't have an access to a even if new local a doesn't even exist yet (because the reassignment isn't finished).
Question: Why [...] the first example works without errors but in the second example the same line of code is broken?
Because in the first example, you're only reading from a global a, then writing to a local b.
In the second example, a is assumed to be a local too (since you're not declaring global a), and it has no value when you're trying to read it in the first line, hence an error.
This is similar to JavaScript's Temporal Dead Zone, if that's familiar.
As for the question in your title:
Why it's impossible to reassign global name within local scope (without global keyword) with using the global name's value in reassignment?
You're not reassigning
a global name at all in either of your examples, since indeed you're not using the global keyword. In Python, you can always read a global (or "ambient") name; otherwise it'd be a pain to e.g. use any builtin function (since they're not locals), and having a special exception for builtins would be, well, a special exception, and those aren't nice.

What is so special about += operator? [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 last year.
I wrote a code similar to the following and it gives me the local variable 'a' referenced before assignment error. When I changed that a += [2] into a.append(2), it worked.
def f():
a = [1]
def f1():
a += [2] # => no error with a.append(2)
f1()
print(a)
Why? Why the parser can't recognize the outside a with +=?
It's an assignment to a. It's basically syntactic sugar for
a = a.__iadd__([2])
The assignment makes a a local variable when the code is generated, but then the RHS of the assignment tries to access that variable before at runtime it is defined.
a.append(2), on the other hand, is not an assignment. a is a free variable whose value is taken from the closest enclosing scope.
If you want to assign to a non-local variable, you need to declare the name as non-local first.
def f():
a = [1]
def f1():
nonlocal a
a += [2]
f1()
print(a)

difference between += and append() in Python [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 1 year ago.
When I use the += in my function, I get this error: UnboundLocalError: local variable 'travel_log' referenced before assignment
but it totally works with the append() function.
What is the difference?
travel_log = []
def add_new_country(countries_visited, times_visited, cities_visited):
new_country = {}
new_country["country"] = countries_visited
travel_log += new_country
Because you assigned to travel_log in your add_new_country function (+= is an assignment), it is considered a local variable in that function. Because it is a local variable, and you never assigned it a value, travel_log has no value when you attempt to += to it. Python does not fall back to using the global variable of the same name.
This is somewhat surprising because the += operation on a list is equivalent to calling its extend() method, and the name remains bound to the same list. The error feels more reasonable when the variable is a number or some other immutable object. But it's the same behavior.
If you want to operate on the global variable, say so:
def add_new_country(countries_visited, times_visited, cities_visited):
global travel_log
# etc.
But it's better to use travel_log.append(). Appending isn't an assignment, so it doesn't make travel_log local.
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in add_new_country() assigns a new value to travel_log, the compiler recognizes it as a local variable.
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be local unless explicitly declared as global.
So in your case, declare travel_log as global scope inside the function
travel_log = []
def add_new_country(countries_visited, times_visited, cities_visited):
# now function will refer the globally declared variable
global travel_log
new_country = {}
new_country["country"] = countries_visited
travel_log += new_country
Checkout this blog for a detailed explanation: https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

Assignment to global vs local variables of different types in Python [duplicate]

This question already has answers here:
Why isn't the 'global' keyword needed to access a global variable?
(11 answers)
Closed 6 months ago.
cache = {}
def func():
cache['foo'] = 'bar'
print cache['foo']
output
bar
Why does this work and why doesn't it require use of the global keyword?
Because you are not assigning to cache, you are changing the dictionary itself instead. cache is still pointing to the dictionary, thus is itself unchanged. The line cache['foo'] = 'bar' translates to cache.__setitem__('foo', 'bar'). In other words, the value of cache is a python dict, and that value is itself mutable.
If you tried to change what cache refers to by using cache = 'bar' instead, you would be changing what cache points to and then you need the global keyword.
Perhaps this older answer of mine to a similar question helps you understand the difference: Python list doesn't reflect variable change.

python variable scope in function [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 8 years ago.
I have found a similar question Python variable scope error. It's related to immutable variable. But when I test mutable variable, I don't know how Python interpreter decides the scope of the variable.
Here's my sample code:
def test_immutable():
a = 1
b = 2
def _test():
print(a)
print(b)
a += 1
print(a)
_test()
def test_mutable():
_dict = {}
def _test():
print(_test.__dict__)
_dict['name'] = 'flyer'
print('in _test: {0}'.format(_dict['name']))
_test()
print(_dict['name'])
if __name__ == '__main__':
# test_immutable() # throw exception
test_mutable() # it's ok
Immutable vs mutable has nothing to do with variable scoping. Variables are just names, and always work the same way. Scoping is even decided at compile time, long before Python knows what you're going to assign to them.
The difference between your two functions is that the first one assigns directly to a with the += operator, which causes a to become a local. The second one assigns to a key inside _dict, which ultimately calls a method on the dict object, and doesn't affect the variable's scoping.

Categories

Resources