Python scope, functions, blocks and non-local [duplicate] - python

This question already has answers here:
Short description of the scoping rules?
(9 answers)
Closed 5 months ago.
I am confused about scope.
What, if anything, is the difference between assigning a variable 'within a function' and assigning one within an
indented block? I have read many places that if and try blocks do not create, or have, their own scope, but I have also
read that the scope of a variable is the innermost block where it is defined.
I googled but was not able to find an example of nonlocal inside an if or try block.
def sixdig2iso(pathfrom):
os.chdir(pathfrom)
filelist = os.listdir()
nonlocal xfrs
xfrs = 0
PyCharm says nonlocal variable 'xfrs' must be bound in an outer function scope
Isn't this the outermost part of this function? Then what's the problem?
Is the outermost part of this function != an outer function? Even if the scopes of each are different from the inner parts of those same functions?!
if xfrs == 0:
restofit = frs[1:]
try:
convert = datetime.strptime(mm, '%m%d%y')
except ValueError as e:
logger.info(f"For {filename}, mm is: {mm} - and the error is: {e}")
count_err += 1
ender = ' '.join(restofit)
fronter = str(convert.date())
PyCharm says the 2nd convert 'might' be used before assignment
I tried making an inner function
def sixdig2iso(pathfrom):
"""Converts six digit dates into proper datetime format in place."""
os.chdir(pathfrom)
filelist = os.listdir()
nonlocal xfrs
xfrs = 0
def blockscope():
But PyCharm gives me the same "nonlocal variable 'xfrs' must be bound in an outer function scope" warning.
UPDATE
My response is too long for a comment.
“We have to guess because you didn't provide a complete example”
I can never seem to get the balance between ‘not enough’ and ‘too much’ information for these questions. I probably didn’t think the part you say is missing was relevant, which goes to my understanding of the problem in the first place.
“ it doesn't matter how many nestings of function you create inside this function, nonlocal only looks outward.“
See, I didn’t know that. And I infer, then, it only looks ‘upward’, too, right?
“As .strptime() might fail with an exception, convert may end up not having been assigned a value, as you did not initialise convert outside the try block. “
Ok, good, that is very helpful. I didn’t realize (and PyCharm does not explain as you just did) this is what PyCharm was talking about. But this is also why I was confused about the ‘scope’ of a try block.

In the first example you give, xfrs is only defined in the function you've provided as an example. We have to guess because you didn't provide a complete example, but the message from PyCharm suggests that you did not define this function inside another function that already had such an identifier defined in it and there's no xfrs in the global scope either.
In the second example, you assign to convert in the try section of a try .. except block. As .strptime() might fail with an exception, convert may end up not having been assigned a value, as you did not initialise convert outside the try block. So, PyCharm is correct (again, we have to assume as you did not provide a complete example.
Finally, in the third example, you start defining a function inside the function, but still apply nonlocal to a variable in the sixdig2iso() scope - it doesn't matter how many nestings of function you create inside this function, nonlocal only looks outward.
A typical use of nonlocal is as #Carcigenicate provides in their link (some modifications here):
x = 0
def outer():
x = 1
def inner():
nonlocal x
x += 1
return x
return x, inner
v, f = outer()
print(x, v, f())
The function returned by outer() produces 2 when called, as its non-local variable x of inner() is the same as the local variable x of outer(), starting at 1 and having 1 added as the function is called.
You can tell that is what happens, as the value returned by outer() is 1, but calling the returned function f() returns 2. All the time, the global x is untouched.
Try changing nonlocal to global and you'll find the the result changes from:
0 1 2
To:
0 1 1
I hope that helps explain what is going on with nonlocal.

Related

assignment of variable after function definition in Python

I am new to Python so I am unsure about the difference in variable assignment before or after the function definition.
Specifically, the first example was adopted from Lutz's book.
def tester(start):
print("inside tester")
def nested(label):
print("inside nested")
print(label,nested.state)
nested.state += 1
print("done with nested")
nested.state = start
print("done with tester")
return nested
F = tester(0)
F('spam')
F.state
F.state
The objective of the code is to store the state information without using nonlocal.
I am unsure what nested.state means here. I am unsure because nested.state is used inside nested() function (i.e. nested.state +=1) and outside nested() function (i.e. nested.state = start).
I modified the code above to see whether Python accepts assigning variable after function declaration for nested() and to see whether there is any concept I am missing relating to function.variable call (i.e. nested.state call).
def tester(start):
def nested(label):
print(label, state)
state += 1 #replaced 'nested.state' with 'state' here
state = start #replaced 'nested.state' with 'state' here
return nested
F=tester(0)
F('spam')
F('ham')
Unfortunately, above code generates error local variable 'state' referenced before assignment. This tells me that I am missing some concept about function.variable (i.e. nested.state).
Can someone please help me understand three things:
I. why it is that the code with nested.state doesn't generate any error but state does?
II. what does nested.state mean? If nested.state is a mechanism to access function's variables, why is it that the call inside nested() function also uses nested.state and not state?
III. If nested.state is a mechanism to access variable inside function, then why is it that PyCharm fails to show state under dropdown when I type nested.?
I'd appreciate any help. I research SO, and couldn't find any explanation on such problems.
The reason the first code example worked is because it was assigning and referencing an attribute of the nested function object. The key concept to understand here, is that Python allows you to assign new, arbitrary attributes to objects - including functions:
>>> def func(a, b):
return a + b
>>> func(1, 2)
3
>>> func.attr = 5
>>> func.attr
5
The first code example takes advantage of this fact by using the nested function object to store the necessary state. This is the same concept as using any other object to store the state. It's particularly convenient, however, to use a function since it's readily available.
In the second example, a normal variable is used. Because of this, normal scoping rules apply which means simply that the state variable defined in tester is not the state variable being referenced in nested. Thus, an error is raised.
Actually, I think you're asking a question about scope in Python, ignoring your code, check this:
def scope_level_1():
variable = 'Scope_level_1'
def scope_level_2():
variable = 'Scope_level_2'
def core():
nonlocal variable
variable += '_TOUCHED_AND_MODIFIED_BY_CORE'
print(variable)
return core()
return scope_level_2()
scope_level_1()
# 'Scope_level_2_TOUCHED_AND_MODIFIED_BY_CORE'
Don't worry about the keyword nonlocal, treat it just as a declaring to make code more readable.
First, remember a += b is the same as a = a + b. So a must exist before getting to the +=.
Simply put, in the first example the function nested has an attribute called state (accessed by nested.state). It is an attribute, which means that once you tell nested that it has an attribute called state (you are doing this in line 9 when nested.state = start) it keep that attribute. So, in the first example nested.state exists when you get to the +=.
In the second example, you are declaring a variable called state in tester, and another variable called state in nested. The one in nested could be called potato for all that matters, because it is not the same variable. Therefore when you arrive to the +=, the variable state does not exist!

Are assignments made at function call in Python?

While building a parameterized decorator, I had not realized reassignments to passed in arguments within nested functions were not allowed in Python. Looking further, I realized this is also true for simple functions as well. I have reduced the demonstration to the following nested function:
def a(s):
def b():
def c():
# nonlocal s # fix
print(s)
# while s:
# s -= 1 # uncommenting raises UnboundLocalError
print(s)
return None
return c()
return b()
a(3)
# 3
# 3
I would like the following desired output by adding the commented while loop:
a(3)
# 3
# 0
Next, uncommenting the two lines of the while loop gives the following error, which suggests that reassigning a value to s raises an error:
<ipython-input-37-7141eb599936> in c()
3 def c():
4 # nonlocal s # fix
----> 5 print(s)
6 while s:
7 s -= 1 # uncommenting raises UnboundLocalError
UnboundLocalError: local variable 's' referenced before assignment
Finally, uncommenting nonlocal fixes this issue and gives the desired output as suggested by this post.
Although the problem is solved, I would like to understand the source of the issue. I noticed the traceback points to the first use of the parameterized argument s (e.g. print(s)), rather than pointing to the lines that actually cause the error (i.e. the while loop/assignment).
I suspect that upon calling a function, Python first establishes assignments of the local scope. Assignments then take higher precedence over or override inherited variables from the outer scopes. Thus without an assignment to s, the outer s is used. By contrast, with an assignment, s is redefined at the function call, and any reference before the initial assignment will raise an error. Is this correct, or can someone explain what Python is actually doing?
If a function contains an assignment to a variable (including augmented assignments such as -=, that variable is automatically local, unless explicitly declared as global (or nonlocal). If there are no assignments, it's automatically global, without needing any declaration (since it could hardly be a local variable when there's no source of a value for it). This analysis is performed before any code is generated, so you get situations like this where a subsequent line of code can cause an earlier line to become an error.

Scope in Python 3.6.And the Error [duplicate]

This question already has answers here:
Function not changing global variable
(4 answers)
Closed 5 years ago.
How does the code below give the output as None?
def funct(x):
x=7
x=11
x=test(x)
print(x)
Here is another code snippet:-
def func():
print (x)
x = 90
x = 1
func()
The output for this should be 90! The scope is searched as Local ,Enclosed , Global ,Build-in .Either x should here be searched as local or global
Please explain.
x here is not a global variable in the scope of the functions, as functions naturally create their own namespaces, which do not include any outer variables not passed in as a parameter.
There are many issues with your code, including the order of function calling, and the order of the operations inside the functions; but to answer your question in the broadest way possible, in order for you to access the x variable defined outside of your functions, in a greater scope that is, you need to reference its namespace, by prepending global x inside the body of each of your functions.
Read up on Python variables and scope, and recheck the other errors in your code I have stated above.
The first code snippet returns None for the simple fact that you didn't return any value from the function. You defined x as a local parameter, not a global variable. Since you didn't return any value, your call test(x) (which does not match the function name of funct), will become a value of None. This is clearly defined in the Python documentation. To get the local value back to your main program, try this:
test(x):
x = 7
return x
x = 11
x = test(x)
print(x)
Also, please note that your initial value of 11 is entirely ignored in the main program, and that your function ignores the value it's given. You should shorten this example to
func():
return 7
print(func())
Your second example will print the external value of 1 because you have not defined a local variable x at that point, and a function is allowed to reference global variables.
However, when you assign a value in the next, you create a local variable -- you have not declared x to be global, so you can't assign to the x in the main program.
Again, you do not return any value. Therefore, back in the main program, func() evaluates to None.
Does that clear up what happened?

What is masking here [duplicate]

This question already has answers here:
masking the built-in variable with its magic behavior?
(2 answers)
Closed 6 years ago.
In the python tutorial:
In interactive mode, the last printed expression is assigned to the variable _. This means that when you are using Python as a desk calculator, it is somewhat easier to continue calculations, for example:
>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06
This variable should be treated as read-only by the user. Don’t explicitly assign a value to it — you would create an independent local variable with the same name masking the built-in variable with its magic behavior.
What is masking here?
In general what is meant by masking or mask variable in python?
THanks
A variable is masked if some other variable with the same name exists, which is preferred. An example is having a global variable x. If you're in a function which also has a local variable x, then the global won't be accessible, without special measures the local variable is preferred whenever you use identifier x inside the function.
It's as if you have two kids in your classroom both called Mary, one of which is closer, and will hear your voice earlier, so always gives the quickest answer. How to give the other girl a chance?
Overwriting/reassigning a previously named identifier in a new scope
let's look at this code for example
def f():
return 3
def g():
f = 5
f()
try:
print(g())
except TypeError:
print(f())
within the scope of g, the identifier f has been reassigned from a function that returns an integer, to just an integer. so when you try calling f inside of g you should get a type error because you can't call an int. however outside of g, f is still a function that returns 3
the interpreter has its own scope so when you assign _ to another value, it forgets it's previous functionality in replace of the new value, until you restart the interpreter, or in this special case delete it with the del keyword
The _ is built-in to the interpreter, it has special meaning. It only acts like a read-only variable. When you read from it, the interpreter replaces it with the last calculated value. If you do something like
_ = 1 + 1
You are actually creating a new variable named _ in the current namespace (try running locals()function before and after creating the variable). Since there is now a variable with the same name as the interpreter built-in _ that variable is accessed first when you do subsequent reads, thus hiding the meaning of the built-in, or masking it.
You can also do what you're doing with Python built-ins as well, such as defining a function parameter namedint. In the the local function namespace, if you tried to call theint() function on a value, it would attempt to use your parameterint as a callable because there is a local variable with that name masking the built-in function int. Some IDEs will warn you when you try to do something like this because in practice it is generally a bad idea to mask the name of a built-in like that because you're eliminating the behavior of that what that symbol is intended for.
Where making comes in its if you do something like
val = 20
def do_stuff(val):
# parameter masks the value defined in outer scope
print('Value of a inside function: {}'.f format(val))
do_stuff(10) # prints 10
This assigns a variable val a value of 20, but when the function do_stuff is called it had it's own local variable namedval. Accessingval from within the function will give you the value passed in, 10 in this case, not 20 from the outer variable because thea within the function masks the one in the outer scope.

Python: Variables are still accessible if defined in try or if? [duplicate]

This question already has answers here:
Short description of the scoping rules?
(9 answers)
Closed 5 months ago.
I'm a Python beginner and I am from C/C++ background. I'm using Python 2.7.
I read this article: A Beginner’s Guide to Python’s Namespaces, Scope Resolution, and the LEGB Rule, and I think I have some understanding of Python's these technologies.
Today I realized that I can write Python code like this:
if condition_1:
var_x = some_value
else:
var_x = another_value
print var_x
That is, var_x is still accessible even it is not define before the if. Because I am from C/C++ background, this is something new to me, as in C/C++, var_x are defined in the scope enclosed by if and else, therefore you cannot access it any more unless you define var_x before if.
I've tried to search the answers on Google but because I'm still new to Python, I don't even know where to start and what keywords I should use.
My guess is that, in Python, if does not create new scope. All the variables that are newly defined in if are just in the scope that if resides in and this is why the variable is still accessible after the if. However, if var_x, in the example above, is only defined in if but not in else, a warning will be issued to say that the print var_x may reference to a variable that may not be defined.
I have some confidence in my own understanding. However, could somebody help correct me if I'm wrong somewhere, or give me a link of the document that discusses about this??
Thanks.
My guess is that, in Python, if does not create new scope. All the variables that are newly defined in if are just in the scope that if resides in and this is why the variable is still accessible after the if.
That is correct. In Python, namespaces, that essentially decide about the variable scopes, are only created for modules, and functions (including methods; basically any def). So everything that happens within a function (and not in a sub-function) is placed in the same namespace.
It’s important to know however that the mere existance of an assignment within a function will reserve a name in the local namespace. This makes for some interesting situations:
def outer ():
x = 5
def inner ():
print(x)
# x = 10
inner()
outer()
In the code above, with that line commented out, the code will print 5 as you may expect. That’s because inner will look in the outer scope for the name x. If you add the line x = 10 though, the name x will be local to inner, so the earlier look up to x will look in the local namespace of inner. But since it hasn’t been assigned yet, you will receive an UnboundLocalError (“local variable 'x' referenced before assignment”). The nonlocal statement was added in Python 3 to overcome one issue from this: The situation where you want to actually modify the x of the outer scope within the inner function.
For more information on name lookup, see this related question.

Categories