Python: calling inner() from outer() - python

I have looked around on SO and surprisingly not found an answer to this question. I assume this is because normally inner/nested functions are used for something in particular (eg. maintaining an environment variable, factories) as opposed to something trivial like I'm trying to use them for. In any case, I can't seem to find any information on how to properly call an inner function from an outer function without having to declare inner() above outer() in the file. The problem is from this problem on HackerRank (https://www.hackerrank.com/challenges/circular-array-rotation/problem).
def circularArrayRotation(a, k, queries):
def rotateArrayRightCircular(arr: list, iterations: int) -> list:
"""
Perform a 'right circular rotation' on an array for number of iterations.
Note: function actually moves last 'iterations' elements of array to front of array.
>>>rotateArrayRightCircular([0,1,2], 1)
[2,0,1]
>>>rotateArrayRightCircular([0,1,2,3,4,5], 3)
[3,4,5,0,1,2]
>>>rotateArrayRightCircular([0,1,2,3,4,5], 6)
[0,1,2,3,4,5]
"""
return arr[-1 * iterations:] + arr[0:-1 * iterations]
k = k % len(a)
a = rotateArrayRightCircular(a, k)
res = []
for n in queries:
res.append(a[n])
return res
The code above does what I want it to, but it's somehow inelegant to me that I have to put the inner function call after the inner function definition. Various errors with different attempts:
# trying 'self.inner()'
Traceback (most recent call last):
File "solution.py", line 52, in <module>
result = circularArrayRotation(a, k, queries)
File "solution.py", line 13, in circularArrayRotation
a = self.rotateArrayRightCircular(a, k)
NameError: name 'self' is not defined
# Removing 'self' and leaving the definition of inner() after the call to inner()
Traceback (most recent call last):
File "solution.py", line 52, in <module>
result = circularArrayRotation(a, k, queries)
File "solution.py", line 13, in circularArrayRotation
a = rotateArrayRightCircular(a, k)
UnboundLocalError: local variable 'rotateArrayRightCircular' referenced before assignment
Any idea how I could include def inner() after the call to inner() without throwing an error?

As a function is executed from top to bottom, and a function is put into existence as the function is processed, what you want is just not possible.
You could put the function before the outer one, making it outer itself, possibly adding some parameters (not necessary here). (BTW, it looks so generic that other parts of the code might want to use it as well, so why not outer?)
But otherwise, you are stuck. It is essetially the same situation as in
def f():
print(a) # a doesn't exist yet, so this is an error
a = 4
Well, you could do it this way:
def circularArrayRotation(a, k, queries):
def inner_code():
k = k % len(a)
a = rotateArrayRightCircular(a, k)
# BTW, instead of the following, you could just do
# return [a[n] for n in queries]
res = []
for n in queries:
res.append(a[n])
return res
def rotateArrayRightCircular(arr: list, iterations: int) -> list:
"""
Perform a 'right circular rotation' on an array for number of iterations.
Note: function actually moves last 'iterations' elements of array to front of array.
>>>rotateArrayRightCircular([0,1,2], 1)
[2,0,1]
>>>rotateArrayRightCircular([0,1,2,3,4,5], 3)
[3,4,5,0,1,2]
>>>rotateArrayRightCircular([0,1,2,3,4,5], 6)
[0,1,2,3,4,5]
"""
return arr[-1 * iterations:] + arr[0:-1 * iterations]
return inner_code()
but I don't see that you gain anything from it.

This is not possible in Python, but is possible in other languages like Javascript and PHP. It is called function hoisting.

Related

Please help me modify my code for max min value

def Goods(t):
max_n = t.index(max(t))
min_n = t.index(min(t))
return max_n,min_n
t = [-125,-164,1237,809,5634,1278,8431]
Goods(t, len(t))
Hello.
I'm trying to find two values ​​in a list and get an index tuple as the result.
The two values ​​are the maximum and the minimum.
(6, 1)
>>> print(Goods([-125,-164,1237,809,5634,1278,8431]))
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
print(Goods([-125,-164,1237,809,5634,1278,8431]))
TypeError: Goods() missing 1 required positional argument: 'n'
I found the value. But what I want is a way to get results when I type "print(Goods([-125,-164,1237,809,5634,1278,8431]))" like this.
I would really appreciate it if you could teach me how to modify the code for the answer I want.
You didn't provide all of the arguments to your function. Should be:
def Goods(t, n):
max_n = t.index(max(t))
min_n = t.index(min(t))
print((max_n,min_n))
buff_list = [-125,-164,1237,809,5634,1278,8431];
Goods(buff_list, len(buff_list))
If you would like to print the values, you have to ask the function to return something. I.e., modify your function to:
def Goods(t, n):
max_n = t.index(max(t))
min_n = t.index(min(t))
return (min_n, max_n)
print(Goods(buff_list, len(buff_list)))

Is there a reduce function for python in which we can access index of current item?

function namesScores(arr) {
// Good luck!
arr.sort();
return arr.reduce((acc, v, i) => acc + wordWorth(v) * (i + 1), 0);
}
In the above code written in javascript the callable function that I have given to the reduce function uses three parameters accumulator, currentItem, currentIndex.
from functools import reduce
def nameScores(arr):
arr.sort()
return reduce(lambda acc, v, i: acc + wordWorth(v) * (i + 1), arr, 0)
While writing the same code in python, I am getting an error:
Traceback (most recent call last):
File "/home/cyogian/practicePython/ProjectEuler/p022/p022.py", line 22, in <module>
print(nameScores(test1))
File "/home/cyogian/practicePython/ProjectEuler/p022/p022.py", line 18, in nameScores
return reduce(lambda acc, v, i: acc + wordWorth(v) * (i + 1), arr, 0)
TypeError: <lambda>() missing 1 required positional argument: 'i'
This says that the currentIndex parameter is missing. Is it that the reduce function in python functools does not give access to the currentIndex?
Is there any other version of reduce in python that give access to the index of current item in the iterable?
Or should I go with simple forLoop in the case where I need access to Index?
You don't need a special form of reduce to access the index: just use the enumerate function on the iterable you want to reduce.
>>> a = ['foo', 'bar', 'baz']
>>> from functools import reduce
>>> reduce(lambda acc, iv: acc + str(iv[0]) + iv[1], enumerate(a), '')
'0foo1bar2baz'
Here, iv is one pair from enumerate, so iv[0] is the index and iv[1] is the value.
Explanation:
The reduce function in Python indeed does not pass the index to the callback. The Javascript Array.reduce method does, and can get away with this because if you pass more arguments than a Javascript function expects, they are just ignored; so your callback function in Javascript can be like (acc, v) => acc + v and it will ignore the index when it's called with (acc, v, i) as its arguments.
Python functions don't silently ignore extra arguments; if you call a function in Python with too many arguments, it raises a TypeError. This means if reduce called your callback function with the arguments (acc, v, i), then you would be required to provide a callback function which accepts the index, even though in the vast majority of cases, you don't need to use the index for anything. That is, if you could ever call reduce(lambda acc, v, i: ..., ..., ...) where the callback takes three arguments, then you would always have to call reduce like that in Python.

Python the function locals() was changed by just calling it

please read the two code ,and i find the only different is printing the locals() or not. But one of them is wrong.
Please help me , thanks
import numpy as np
class Solution:
def solve(self, f, a, b, n):
x = np.linspace(a,b,n)
# print(locals())
loc = locals()
fstr = '''
def fun(x):
return %s
''' % f
exec(fstr)
# print(locals())
fun = loc['fun']
y = fun(x)
print(x,y,sep = '\n')
a = Solution()
a.solve('x+1',-5,5,5)
In this code ,I didn't print the locals()
if I only print it and write '#' in front of "fun = loc['fun']" and "y = fun(x)" ,there is a key named 'fun' in the output of locals()
import numpy as np
class Solution:
def solve(self, f, a, b, n):
x = np.linspace(a,b,n)
# print(locals())
loc = locals()
fstr = '''
def fun(x):
return %s
''' % f
exec(fstr)
print(locals())
fun = loc['fun']
y = fun(x)
print(x,y,sep = '\n')
a = Solution()
a.solve('x+1',-5,5,5)
But in this code ,i can't find the key named 'fun' in the output of locals()
Traceback (most recent call last):
File "tmp.py", line 20, in <module>
a.solve('x+1',-5,5,5)
File "tmp.py", line 15, in solve
fun = loc['fun']
KeyError: 'fun'
All of this seem that "fun = loc['fun']" and "y = fun(x)" determine the output of locals()
but i think it is impossible for python that latter code can change the front code
Yeah, that happens with locals(). locals() is confusing and not well documented.
Calling locals() repeatedly in the same stack frame returns the same dict every time, and every call to locals() updates that dict with the current values of local (or closure) variables. The dict is attached to the stack frame as its f_locals attribute, and accessing that attribute will also update the dict.
To use locals() safely without the values changing unpredictably, you should copy the returned dict:
current_locals = locals().copy()
Otherwise, even running your code in a debugger could change its behavior, since debuggers typically access f_locals to inspect local variables.
Also, trying to exec code that assigns any variables in a local scope is officially unsupported and behaves weirdly, and def counts as an assignment. You shouldn't use exec for this anyway.

Apply function to permutations of lists of arguments

I have the need to take a function of n parameters and n lists of values and apply the function to each possible permutation of arguments. I have looked in itertools and none of the functions are quite right. The following is my attempt. Can someone explain what I am doing wrong? Thanks.
def CrossReference(f, *args):
result = []
def inner(g, *argsinner):
rest = argsinner[1:]
a = argsinner[0]
if type(a) is not list:
a = [a]
for x in a:
h = partial(g, x)
if len(rest) > 0:
inner(h, rest)
else:
result.append(h())
inner(f, args)
return result
Here is my example test and error:
def sums(x,y,z):
return x+y+z
CrossReference(sums, [1,2,3], 4, [5,6,7])
Traceback (most recent call last): File "", line 1,
in File "", line 13,
in CrossReference File "", line 12, in inner
TypeError: sums() takes exactly 3 arguments (1 given)
The problem is in the way you call your inner function. You define your function header as:
def inner(g, *argsinner):
But you call your function like:
inner(f, args)
And:
inner(h, rest)
This means that you will end up with a single tuple (monotuple?) containing the tuple of your args. You can either change your function definition to:
def inner(g, argsinner):
Or change your calling to:
inner(h, *rest)
def sums(x,y=0,z=0):
return x+y+z
def apply(fn,*args):
for a in args:
try:
yield fn(*a)
except TypeError:
try:
yield fn(**a)
except TypeError:
yield fn(a)
print list(apply(sums,[1,2,3], 4, [5,6,7]))
is one way you might do it (not the only way though)

sqrt() argument after * must be a sequence

First of all, I'm super new to python and I actually search for my problem but the examples were to heavy to understand.
Here is my homework; I need a function which takes two functions as an argument and returns if the results of the two functions are same or not? Basically, it will give either TRUE of FALSE.
For that I wrote:
def f(x,y,z):
k=x(*z)
l=y(*z)
return k == l
The previos code I wrote for single function was working but when I modified it for two function as above, it gives an error as following :
import math
>>> f(math.sqrt,math.cos,5)
Traceback (most recent call last):
File "<pyshell#56>", line 1, in <module>
f(math.sqrt,math.cos,5)
File "D:/Users/karabulut-ug/Desktop/yalanmakinesi.py", line 2, in f
k=x(*z)
TypeError: sqrt() argument after * must be a sequence
>>>
I could not figured it out since the error giving function is normally does not take a sequence. So I dont think it makes a sense :) Any help is appreciated.. Thanks :)
z is just a single number, but the * argument expansion syntax requires that you pass in a sequence (like a list, tuple or str, for example).
Either remove the * (and make your function work for just single arguments), or use *z in the function signature to make z a tuple of 0 or more captured arguments:
def f(x, y, z):
k = x(z)
l = y(z)
return k == l
or
def f(x, y, *z):
k = x(*z)
l = y(*z)
return k == l
The latter now works for functions with more than one argument too:
f(math.pow, math.log, 10, 10)
If you added a **kw argument to the signature, then keyword arguments could be handled too:
def f(x, y, *args, **kwargs):
k = x(*args, **kwargs)
l = y(*args, **kwargs)
return k == l
Here I renamed z to args to better reflect its purpose.
The syntax *z invokes argument unpacking on z. When z is just an integer, there is no iterator behavior defined, and so you see this error. Try:
>>> f(math.sqrt, math.cos, [5])
You need to remove the *. Its for unpacking. So:
def f(x,y,z):
k=x(z)
l=y(z)
return k == l
You use the * operator when you want to pass in an iterable object, like a list or tuple as something thats split up. So, for example:
a = [1,2,3,4,5]
So, for an arbitrary function, f:
f(*a) = f(1,2,3,4,5)

Categories