Python: Nested function naming conflict - python-3.x

I recently encountered an error with the following python code:
def foo():
print("Global")
def func():
foo() # This causes an error, but only if foo() is redefined
def foo():
print("Nested")
func()
I naively expected this to print "Global", since the call to foo() occurs before it is redefined within the scope of func(). However, that line raises this error:
UnboundLocalError: local variable 'foo' referenced before assignment
It seems like the interpreter treats the call to foo() differently based on the future naming conflict, and it has to due with this being within a function since in-lining the call to foo() and the redefinition doesn't have the same behavior.
QUESTION: What is happening under the covers in this scenario? I've found several other questions/answers regarding local vs global variables but nothing regarding local functions like this. Thanks!
ANSWER: The interpreter first determines whether each variable in a function is local or global based on its use before it executes the function call. The act of defining foo() inside func() makes it local, so the call to it breaks. The same holds true for this simpler example:
x = 9
def func():
print(x)
x=5 # This statement makes 'x' local, so the previous line throws
func()

This is because the Python compiler looks at all the l-values in a code block when determining which variables are local at compilation time rather than at run time, so the fact that foo is defined in func anywhere makes foo local to that code block. You there can't reference it before it is defined in func.

Related

Declaring variables inside a function python

For some reason I keep getting error in my code stating that my variables have not been declared. This only happens when I try to declare them in a function and not outside.
example
x, y = 105,107
print (x,y)
the above line of code works and gives me the output 105 107
but when I do this below
def fun1():
x, y = 105,107
print (x,y)
I get NameError: name 'x' is not defined
need help to understand what's happening.
One of the main utilities of functions is exactly the way they allow one
to isolate variables - no worries about clashing names for the code
in various functions - once a function works properly, it is done.
But if one needs to expose variables that populated inside functions to
the outside world, it is possible with the keyword "global". Notice that this
is in general considered bad practice, and even for small scripts,
there are often better solutions. But, common sense should always be the rule:
def fun1():
global x, y
x, y = 105, 107
fun1()
print(x, y)
Note that your code had another incorrect assumption: code
inside function bodies is not executed unless the function is called -
so, in the example in your question, even if you had declared
the variables as global, the print call would still
raise the same error, since you are not executing the line
that defines these variables by calling the function.
Now, you've learned about "globals" - next step is forget it
exists and learn how to work with variables properly encapsulated
inside functions, and when you get to some intermediate/advanced
level, then you will be able to judge when "globals" might actually
do more good than harm (which is almost never).

How a function in python can access the values which are declared outside its block and never passed as arguments during its call?

I have written a Python code in which a function without parameters can access the variable declared outside its scope. I want to know how Python interpreter can access this variable without giving any error like other programming languages(e.g. JAVA).
# Code 1:
def A():
# count accessed inside function
print(count)
# count declared outside function A
count = 23
A()
Output of code 1:
23
There is a bonus question also. In Python, if we declare any variable inside the loop then how it can used outside the loop. Because, as we know that the scope of any variable will remain under the block in which it is defined.
# Code 2:
for i in range(1):
# num declared inside for loop block
num = 23
# num can be accessed outside for loop block.
print(num)
Output of code 2: 23
Variable scope is inside-out. When you call a variable from inside a local scope like a function, first Python checks if there's a variable with that name in the local scope. If it doesn't find one, it expands the scope it checks in to the next outer scope. In your case, this is the global scope, the top-most level of scope. Declaring something absent of any context like a function or method defaults to the global scope, so that's where count lives.
If you want to be clear about which count it is you're referencing, you can add nonlocal count or global count to your function definition, making it explicit that you're referencing the a different scope.
Bonus question -- You can't reference a locally-scoped variable outside of its scope, so it doesn't work in reverse, sorry. However in your example you're using a loop, which does not have its own scope in Python (but Julia does, so if you ever make that switch heads up).

Why Unbound Local error is showing up, where I expect NameError should be raised?

When I delete the variable and access it inside function the unboundLocalError shows up where as if do the same thing outside the function NameError is raised.I am not able to understand why there is inconsistency in behaviour to access the deleted Variable?
def func():
x=10
del x
print(x)
func() #this will cause UnboundLocal Error
#But if i copy the same code and execute it without using the function call then NameError shows up
x=10
del x
print(x) #this will raise NameError as x does not exist
According to me in both cases NameError should be raised as we are trying to access the variable after deleting the variable.
Raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable. This is a subclass of NameError.
https://docs.python.org/3/library/exceptions.html?highlight=unboundlocalerror
The difference seems to be one of purely location. UnboundLocalError extends the NameError and UnboundLocalError is chosen as the error when it occurs inside a method/function.
This might not settle a separate question: "Why did python dev's decide to do this this way" But it does explain why you're seeing it.

Scope of Functions as objects in Python

I am trying to understand how python stores and accesses its functions as objects .
For example take the following code-
def make_pretty(func):
def inner():
print("I got decorated")
func()
return func
return inner
def ordinary():
print("I am ordinary 2")
ordinary()
pretty=make_pretty(ordinary)
pretty()
When executed , pretty() returns
I got decorated
I am ordinary 2
<function __main__.ordinary()>
which seems to imply that the ordinary() function passed to make_pretty() is accessed as in the main scope, and as dir(pretty) doesn't show ordinary (Please correct me if i am wrong).
Now if I run this code after this
def ordinary():
print("I am ordinary 3")
pretty()
I still get the same output as before , even though I have the changed the global definition of ordinary , i.e. pretty still is considering the previous definition of ordinary even though I have redefined ordinary and the func in pretty is referring to global ordinary
Why is this so? I clearly am wrong somewhere but I don't understand where.
Some insight would be appreciated. Thanks
even though I have the changed the global definition of ordinary ,
i.e. pretty still is considering the previous definition of ordinary
even though I have redefined ordinary and the func in pretty is
referring to global ordinary
You didn't change the function object that pretty refers to. You created a new function, and gave the name ordinary to it. The original function still exists, but can no longer be referred to by the name ordinary.
pretty doesn't know about all this. It refers to the original function all the time.
Before
Both the name "ordinary" and the function pretty refer to the same function:
<function 1> <-- "ordinary"
<-- pretty
After
The name "ordinary" refers to a new function, but pretty still refers to the original function:
<function 1> <-- pretty
"ordinary" --> <function 2>

Calling functions with variables multiple times

I'm making a program for a school project and I'm having an issue.
I have defined a function called DidThisWork like so:
def DidThisWork():
global DidThisWork
DidThisWork = input('\n\nDid this solution work? - ').lower()
Throughout my code, I want to call this function multiple times, however, I'm not able to. Is there a way, to call it multiple times, and like reset the DidThisWork variable inside the function after I used it in if statements?
You define a function def DidThisWork(): then within that very function you overwrite the newly created DidThisWork variable (which points to the function) to the result of your input(..) call.
So at the first call to DidThisWork(), the DidThisWork variable no longer points to the function, rather to the string returned by input(...).
If you rename either the function or the variable storing the string returned by input() it should work.
By the way, there are some naming conventions in Python you may want to look into https://www.python.org/dev/peps/pep-0008/#id30. Typically you'd use snake_case instead of camelCase and not only start a class with an upper case letter
worked = None
def did_this_work():
global worked
worked = input('\n\nDid this solution work? - ').lower()
print(worked)
did_this_work()
print(worked)
did_this_work()
print(worked)

Resources