Scope of Functions as objects in Python - python-3.x

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>

Related

PyQT QPushButton Connection Problem - Wrong function being called [duplicate]

Im trying to build a calculator with PyQt4 and connecting the 'clicked()' signals from the buttons doesn't work as expected.
Im creating my buttons for the numbers inside a for loop where i try to connect them afterwards.
def __init__(self):
for i in range(0,10):
self._numberButtons += [QPushButton(str(i), self)]
self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i))
def _number(self, x):
print(x)
When I click on the buttons all of them print out '9'.
Why is that so and how can i fix this?
This is just, how scoping, name lookup and closures are defined in Python.
Python only introduces new bindings in namespace through assignment and through parameter lists of functions. i is therefore not actually defined in the namespace of the lambda, but in the namespace of __init__(). The name lookup for i in the lambda consequently ends up in the namespace of __init__(), where i is eventually bound to 9. This is called "closure".
You can work around these admittedly not really intuitive (but well-defined) semantics by passing i as a keyword argument with default value. As said, names in parameter lists introduce new bindings in the local namespace, so i inside the lambda then becomes independent from i in .__init__():
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
UPDATE: clicked has a default checked argument that would override the value of i, so it must be added to the argument list before the keyword value.
A more readable, less magic alternative is functools.partial:
self._numberButtons[i].clicked.connect(partial(self._number, i))
I'm using new-style signal and slot syntax here simply for convenience, old style syntax works just the same.
You are creating closures. Closures really capture a variable, not the value of a variable. At the end of __init__, i is the last element of range(0, 10), i.e. 9. All the lambdas you created in this scope refer to this i and only when they are invoked, they get the value of i at the time they are at invoked (however, seperate invocations of __init__ create lambdas referring to seperate variables!).
There are two popular ways to avoid this:
Using a default parameter: lambda i=i: self._number(i). This work because default parameters bind a value at function definition time.
Defining a helper function helper = lambda i: (lambda: self._number(i)) and use helper(i) in the loop. This works because the "outer" i is evaluated at the time i is bound, and - as mentioned before - the next closure created in the next invokation of helper will refer to a different variable.
Use the Qt way, use QSignalMapper instead.

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).

Python: Nested function naming conflict

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.

Enter new variable as argument

I want to create a function that takes some chain of characters as an argument, and uses it as a str object.
def useless_function(argument) :
print(argument)
useless_function(banana)
--> NameError: name 'banana' is not defined
So this is what I did : I created a decorator that turns whatever I enter as argument into a str my function can print.
def decorator(f) :
def wrapper(arg_f) :
str_arg = str(arg)
f(str_arg)
return wrapper
So now I can decorate useless_function with my decorator, and useless_function(banana) will print 'banana'. And it will work with whatever it enter as an argument of useless_function.
My question is : is there a more elegant way or a simpler and faster way to do this automatic transformation into a string that can be used as an argument ?
Can you please elaborate because I don't understand what it is that you are looking for or saying.
If you mean: inside a function can you do input("variable")? Then the answer is yes. It is just essentially raw_input() from python2. The input from your keyboard will always be a str if I am not mistaken.
Update after edited post:
It is still not any more clear what you are trying to do.
At the end of the function, you do return * but I assume you know this.
I am really confused, but have you considered just doing str(argument)? As in takes_argument(str(argument))
2nd Update after 2nd edit:
I think I finally understand what you are trying to do, but I might be wrong.
Now, the problem is that def useless_function(argument) : will expect argument to be defined as a variable with some value(s). I am not aware of any other way than actually putting "argument" to tell python that what you are inserting is a string of characters rather than trying to reference some variable and its value. It is the same case as with print('something'), if I were to put print(something), python would try to look up the variable called something which you haven't defined.
Hope that makes sense.

Call nested Python 3 nested function from parent function

For Python 3. I want to call a nested function from a top-level function. NOT access a variable in a nested function but call a nested function (what I'd normally refer to as a subroutine) from a "parent" function.
Answers on SO and elsewhere describe how to use the global and nonlocal keywords to enable variables in nested functions to be accessed by "parent" functions. But I haven't been able to translate that technique to Python 3 nested functions.
What I'm hoping to achieve, largely for outer-to-inner readability, is:
def topLevelFunction(listOfStrings):
# Top-level function's code here.
desiredValue = nestedFunction(dataToModify)
return(desiredResult)
# This nested function's source code is visibly contained within its parent.
def nestedFunction(oneListEntry):
# Modify data passed to function.
return(fixedData)
This structure of course produces UnboundLocalError: local variable 'nestedFunction' referenced before assignment.
I've circumvented that with:
def topLevelFunction(listofStrings):
def nestedFunction(oneListEntry):
# nestedFunction's code goes here.
return(fixedData)
# topLevelFunction's code goes here.
# Only in this "upside down" structure can top-level function call nestedFunction?
return(desiredResult)
Part of the problem seems to be that the nonlocal / global keywords that enable me to reference variables outside of nested functions' scope haven't enabled me to do the same thing for nested functions themselves(?) Or if they do, the syntax is unique? If that's the case, thanks for a pointer to that specific syntax.
I've also made nestedFunction a stand-alone function at the same level / scope as topLevelFunction. But at least from a readability perspective both circumventions (I won't call them fixes) seem to require me to write "upside down" code where things that are used later in the program flow must be "higher" in the source code?
Perhaps I'm too accustomed to compiled languages that don't require this? Or must I instead create a Python 3 class?

Resources