Closures in Python - executing an inner function? - python-3.x

Why is this in Python3 executing the inner function?
def outer():
out_var = 1
def inner():
inn_var = 2
res = out_var + inn_var
print(res)
return inner
x = outer()
y = outer()
The output is
3
3
Shouldn't it the inner func be executed and the result printed only when executing x() and y()?

Related

modify a main variable from a function that is inside another function

I want to modify variables "player_row" and "player_column" from the function "second_function" which is inside the function "first_function".
If I modify those variables from first_function it works, but my program requires the function "second_function" modifies those variables.
Code:
#functions
def first_function(a, b):
second_function(a, b)
print(f"Inside first_function: {a}, {b}")
def second_function(x, y):
x = 5
y = 10
print(f"Inside second_function: {x}, {y}")
#Main
#Variables
player_row = 0
player_column = 0
first_function(player_row, player_column)
print(f"Inside MAIN: {player_row}, {player_column}")
Result:
Inside second_function: 5, 10
Inside first_function: 0, 0
Inside MAIN: 0, 0
This is because Python is a "pass-by-value" language. Instead of passing a reference that you could change to refer to another value, you pass the value itself.
You have two options: one is to return a value out of your functions that represents the new state of your program, the other is to pass in a value that can be modified. (The difference this works and your original code does not is because the value will still be the same object, in that it will occupy the same memory, but you will have changed the contents of that memory)
Returning a value:
def func_1(row, col):
return func_2(row, col)
def func_2(row, col):
return 5, 10
row, col = func_1(0, 0)
Modifying a mutable object:
from dataclasses import dataclass
#dataclass
class Player:
row: int = 0
col: int = 0
def func_1(player):
func_2(player)
def func_2(player):
player.row = 5
player.row = 10
player = Player()
func_1(player) # Player(row=5, col=10)
You can use this to change the value of variable from another function:
#functions
def first_function(a, b):
a,b = second_function(a, b)
print(f"Inside first_function: {a}, {b}")
def second_function(x, y):
x = 5
y = 10
print(f"Inside second_function: {x}, {y}")
return (x,y)
#Main
#Variables
player_row = 0
player_column = 0
first_function(player_row, player_column)
print(f"Inside MAIN: {player_row}, {player_column}")
You can use a similar return statement in first function as well to changethe value of variables in MAIN

How do I return a value from a higher-order function?

guys how can I make it so that calling make_repeater(square, 0)(5) return 5 instead of 25? I'm guessing I would need to change the line "function_successor = h" because then I'm just getting square(5) but not sure what I need to change it to...
square = lambda x: x * x
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
def make_repeater(h, n):
iterations = 1
function_successor = h
while iterations < n:
function_successor = compose1(h, function_successor)
iterations += 1
return function_successor
it needs to satisfy a bunch of other requirements like:
make_repeater(square, 2)(5) = square(square(5)) = 625
make_repeater(square, 4)(5) = square(square(square(square(5)))) = 152587890625
To achieve that, you have to use the identity function (f(x) = x) as the initial value for function_successor:
def compose1(h, g):
"""Return a function f, such that f(x) = h(g(x))."""
def f(x):
return h(g(x))
return f
IDENTITY_FUNCTION = lambda x: x
def make_repeater(function, n):
function_successor = IDENTITY_FUNCTION
# simplified loop
for i in range(n):
function_successor = compose1(function, function_successor)
return function_successor
if __name__ == "__main__":
square = lambda x: x * x
print(make_repeater(square, 0)(5))
print(make_repeater(square, 2)(5))
print(make_repeater(square, 4)(5))
and the output is
5
625
152587890625
This isn't most optimal for performance though since the identity function (which doesn't do anything useful) is always part of the composed function, so an optimized version would look like this:
def make_repeater(function, n):
if n <= 0:
return IDENTITY_FUNCTION
function_successor = function
for i in range(n - 1):
function_successor = compose1(function, function_successor)
return function_successor

Get output from functions which are run in parallel

I have two functions which I am running asynchronously using python multiprocessing. I need to store values from each function in an array, to use it later.
I have tried to store the output in a numpy array in each of the functions and return them. However, I am not getting any output. The print statements are working alright.
x = np.array([])
y = np.array([])
def func1():
for _ in range(1,150):
print((rstr.xeger(random.choice(sList))))
np.append(x,rstr.xeger(random.choice(sList)))
return x
def func2():
for _ in range(1,150):
print(fake.name())
np.append(y,fake.name())
return y
if __name__ == '__main__':
p1 = multiprocessing.Pool(5)
p2 = multiprocessing.Pool(5)
print("Started")
start = time.time()
x = p1.apply_async(func1)
y = p2.apply_async(func2)
print(time.time() - start)
print("Ended")
I need the arrays x and y which are being returned from the two functions.

Error in regards to positional arguments in python while performing function overloading

I have been implementing function overloading using function to find the area of various figures(that includes square,circle and rectangle) but when I try to implement the concept here is the error that I get.
I made an instance of the class and then tried to call all the function with difference in the number of parameters. But still I am getting a lot of error.
from abc import abstractmethod
class Poly:
def __init__(self,n=0):
self._n = n
def area(self,a):
if isinstance(r,int):
return a ** 2
def area(self,b,pi):
if isinstance(a,float):
ar = pi * b * b
return ar
def area(self,l,r,ar):
if isinstance(l,int) and isinstance(r,int):
ar = l * r
return l*r
if __name__ == '__main__':
p = Poly()
pi = 3.14
ar = 0
l = 3
r = 4
b = 10
a = 2
peroi1 = p.area(a)
print(peroi1)
peroi2 = p.area(b,pi)
print(peroi2)
peroi3 = p.area(l,r,ar)
print(peroi3)
The expected output should give the area of square,circle and rectangle respectively but I get the following error.
Traceback (most recent call last):
File "overloadpractice.py", line 28, in <module>
peroi1 = p.area(a)
TypeError: area() missing 2 required positional arguments: 'r' and 'ar'
You can do something like this, but there are better ways. I'll come up with a better example in a little while:
class Poly:
def __init__(self, n=0):
self._n = n
def area(self, *args):
if len(args) == 1:
if isinstance(a, int):
return r ** 2
elif len(args) == 2:
if isinstance(a, float):
return pi * b * b
elif len(args) == 3:
if isinstance(l, int) and isinstance(r, int):
ar = l * r
return l*r
else:
return 'invalid number of arguments'
if __name__ == '__main__':
pi = 3.14
ar = 0
l = 3
r = 4
b = 10
a = 2
p = Poly()
peroi1 = p.area(a)
print(peroi1)
peroi2 = p.area(b, pi)
print(peroi2)
peroi3 = p.area(l, r, ar)
print(peroi3)
A better(easier to test and maintain) example:
from math import pi
SQUARE = 1
CIRCLE = 2
RECTANGLE = 3
get_area = {
SQUARE: lambda side : side * side,
CIRCLE: lambda radius, _ : pi * radius * radius,
RECTANGLE: lambda length, width, _ : length * width
}
class Poly:
def __init__(self, n=0):
self._n = n
def area(self, *args):
return get_area.get(len(args), lambda: 'invalid number of arguments')(*args)
if __name__ == '__main__':
ar = 0
l = 3
r = 4
b = 10
a = 2
# moved pi to an import
square = Poly()
circle = Poly()
rectangle = Poly()
print(square.area(a)) # 4
print(circle.area(b, pi)) # 14.1592653589793
print(rectangle.area(l, r, ar)) # 12
print(square.area()) # invalid number of variables

Python function - why is it called twice?

In the following Code the function g() is called twice, but why? Is it because in function f() that x=g and than function x() is called but in fact it is g()?
x = 99
def f():
x = g
x()
x = 17
def g():
global x
x = 14
g()
f()
You can also check this by simply adding a print statement as in the following:
x = 99
def f():
x = g
print(x)
x()
x = 17
def g():
global x
x = 14
g()
f()
Output would be: <function g at 0x7f2147265c80> which shows that printing x in line 4 is indeed calling the function g()

Resources