Passing arguments to function after parenthesis - python

I have problem in understanding this code in Python
x = layers.Flatten()(last_output)
Since Flatten is a function, how does the function get the data from last_output written outside the function call parenthesis. Don't remember seeing this kind of code in Java.
Thanks and Regards

Flatten() is the class instantiation (which is probably clear to you) and the second calls the instance with that parameter. For this to work the class must have a __call__ function defined.
Example:
class Sum:
def __call__(self, a, b, c):
return a + b + c
s = Sum()
print(s(3, 4, 5))
print(Sum()(3,4,5))
Also same behavior can be obtained with a function that returns another function with arguments:
def Sum2():
def Sum3(a, b, c):
return a + b + c
return Sum3
s2 = Sum2()
print(s2(3, 4, 5))
print(Sum2()(3, 4, 5))

Consider this
def outer():
def print_thrice(string):
for _ in range(3):
print (string)
return print_thrice
If you call outer, it will return the print_thrice function which you can then call. So you'd use it like this
x = outer()
x("hello")
Or more compactly, outer()("hello") which is what's going on here.

Related

Change only certain arguments in a class/method, hold others constant

I have a class & method, each with several arguments: my_class(a,b,c).my_method(d,e,f) and I'd like to be able to only change a single argument, while holding the others constant.
Constantly copy-pasting the other constant arguments seems bad, so I'd like to create a new object wrapper_fct where I reference my_class but only provide the one argument I want to change, b, without always having to specify the remaining arguments. How would wrapper_fct() look like?
For example, wrapper_fct(my_class, b1) would return my_class(a,b1,c).my_method(d,e,f), wrapper_fct(my_class, b2) would return my_class(a,b2,c).my_method(d,e,f).
Here's an example in practice:
Loop through just the variable b and evaluate several classes/methods for each new instance of b, and append the results in a list.
I can currently do this in a for loop:
mylist1 = [] # init lists (append results here)
mylist2 = []
mylist2 = []
for b in [1,2,3,4,5]:
mylist1.append( my_class1(a,b,c).my_method(d,e,f) )
mylist2.append( my_class2(a,b,c).my_method(d,e,f) )
mylist3.append( my_class3(a,b,c).my_method(d,e,f) )
...
But it seems better to create a function loop_through_B() and use the wrapper_fct(my_class,b) as specified above. Not sure if it's the ideal solution, but maybe something like:
def loop_through_B(input_class, b_values = [1,2,3,4,5])
mylist = []
for b in b_values:
mylist.append( wrapper_fct(input_class,b) )
return mylist
loop_through_B(my_class1) # would I also have to specify the method here as well?
loop_through_B(my_class2)
loop_through_B(my_class3)
Extra Question: how would I add the ability to vary method arguments, or even multiple class & method arguments?
After #chepner pointed me in the right direction, I think the best solution is to use the lambda function:
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
In this case, I can vary b as much as I want while holding the class arguments a,c, and method arguments d,e,f constant. Note that with lambda functions, I can also vary the method arguments and/or the class arguments. For example:
wrapper_fct_multiple = lambda b, e: my_class1(a,b,c).my_method(d,e,f)
It is also possible to do this with functools.partial, but it's not obvious to me how I would specify both class & method arguments with functools.
Anyway, here is the solution implementation using lambda:
# define the "wrapper function" outside the loop
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
# define the function I want to use to loop through B:
def loop_through_B(class_wrapper, b_values)
mylist = []
for b in b_values:
mylist.append( class_wrapper(b) )
return mylist
# run:
loop_through_B(wrapper_fct, b_values=[1,2,3,4,5])
# Can make additional wrapper_fct2, wrapper_fct3, for my_class2, my_class3 ...
You can pass the method a dictionary of arguments, and change what the method sees by selectively updating it when calling the method.
Here's what I mean:
class MyClass:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def my_method(self, kwargs):
return sum((kwargs[key] for key in kwargs.keys()))
def __repr__(self):
classname = type(self).__name__
args = ', '.join((f'{v!r}' for v in (self.a, self.b, self.c)))
return f'{classname}({args})'
instance = MyClass('a','b','c')
print(instance) # -> MyClass('a', 'b', 'c')
kwargs = dict(d=1, e=2, f=3)
print(instance.my_method(kwargs)) # -> 6
print(instance.my_method(dict(kwargs, e=38))) # -> 42

What is the syntax for the input for a def function with multiple nested functions?

I'm learning Python right now and I am just trying to get to grips with all of the syntax options.
Currently, the only thing that I can't seem to google up is what to do if I for some reason want to define a function which contains multiple other defines.
While I understand what to do if there's only 1 define inside the the larger define (val = f()(3,4) returns 7 if you exclude the second def below), I don't know how to correctly use the function below.
If it's possible, what is the syntax for a def function with an arbitrary amount of defined functions within it?
Code:
def f():
def x(a,b):
return a + b
return x
def y(c,d):
return c + d
return y
val = f()(3,4)(5,6)
print(val)
I expected the above to return either (7,11) or 11. However, it returns 'int object is not callable'
When you write val = f()(3,4)(5,6), you want f to return a function that also returns a function; compare with the simpler multi-line call:
t1 = f()
t2 = t1(3,4)
val = t2(5,6)
The function f defines and returns also has to define and return a function that can be called with 2 arguments. So, as #jonrsharpe said, you need more nesting:
def f():
def x(a, b):
def y(c, d):
return c + d
return y
return x
Now, f() produces the function named x, and f()(3,4) produces the function named y (ignoring its arguments 3 and 4 in the process), and f()(3,4)(5,6) evaluates (ultimately) to 5 + 6.

Closure after function definition

Is it possible to define a closure for a function which is already defined?
For example I'd like to have a "raw" function and a function which already has some predefined values set by a surrounding closure.
Here is some code showing what I can do with a closure to add predefined variables to a function definition:
def outer(a, b, c):
def fun(d):
print(a + b + c - d)
return fun
foo = outer(4, 5, 6)
foo(10)
Now I want to have a definition of fun outside of a wrapping closure function, to be able to call fun either with variables from a closure or by passing variables directly. I know that I need to redefine a function to make it usable in a closure, thus I tried using lambda for it:
def fun(a, b, c, d): # raw function
print(a + b + c - d)
def clsr(func): # make a "closure" decorator
def wrap(*args):
return lambda *args: func(*args)
return wrap
foo = clsr(fun)(5, 6, 7) # make a closure with values already defined
foo(10) # raises TypeError: fun() missing 3 required positional arguments: 'a', 'b', and 'c'
fun(5, 6, 7, 10) # prints 8
What I also tried is using wraps from functools, but I was not able to make it work.
But is this even possible? And if yes: Is there any module which already implements decorators for this?
You can just define the wrap on the fly:
def fun(a, b, c, d): # raw function
print(a + b + c - d)
def closed(d): fun(5,6,7,d)
closed(10)
You can use this with lambda, but #juanpa points out you should not if there is no reason to. The above code will result in 8. This method by the way is not Python specific, most languages would support this.
But if you need a closure in a sense that it relies on the wrapper variables, than no, and there is good reason not to. This will create essentially a non-working function, that relies on wrapping. In this case using a class maybe better:
class fun:
def __init__(self,*args): #Can use specific things, not just *args.
self.args = args #Or meaningful names
def __call__(self,a, b, c, d): # raw function
print(a + b + c - d,self.args)
def closed(d):
fun("some",3,"more",['args'])(5,6,7,d)
closed(10)
or using *args/**kwargs directly and passing extra variables through that. Otherwise I am not familiar with a "inner function" construct that only works after wrapping.

Python functions with multiple parameter brackets

I've been having trouble understanding what h(a)(b) means. I'd never seen one of those before yesterday, and I couldn't declare a function this way:
def f (a)(b):
return a(b)
When I tried to do def f (a, b):, it didn't work either. What do these functions do? How can I declare them? And, finally, what's the difference between f(a, b)and f(a)(b)?
Functions with multiple parameter brackets don't exist, as you saw when you tried to define one. There are, however, functions which return (other) functions:
def func(a):
def func2(b):
return a + b
return func2
Now when you call func() it returns the inner func2 function:
>>> func2 = func(1) # You don't have to call it func2 here
>>> func2(2)
3
But if you don't need the inner function later on, then there's no need to save it into a variable and you can just call them one after the other:
>>> func(1)(2) # func(1) returns func2 which is then called with (2)
3
This is a very common idiom when defining decorators that take arguments.
Notice that calling func() always creates a new inner function, even though they're all named func2 inside of the definition of our func:
>>> f1 = func(1)
>>> f2 = func(1)
>>> f1(1), f2(1)
(2, 2)
>>> f1 is f2
False
And, finally, what's the difference between f(a, b)and f(a)(b)?
It should be clear now that you know what f(a)(b) does, but to summarize:
f(a, b) calls f with two parameters a and b
f(a)(b) calls f with one parameter a, which then returns another function, which is then called with one parameter b
f(a)(b) just means that the expression f(a) returns a value that is itself callable. It's a short form of
g = f(a)
g(b)
You might be more comfortable adding a pair of redundant parentheses to emphasize that this is not a single syntactic construct.
(f(a))(b) # f(a) is evaluated first, then the result is applied to b
It is exactly analogous to the same doubling of square brackets for indexing nested dictionaries.
d1[x][y]
is equivalent to
d2 = d1[x]
d2[y]
Lets say we have an expression like
f(a)(b)
then, f(a) returns a function itself which gets invoked with argument b. Consider the following example
def f(a):
def g(b):
return a * b
return g
Then f(5)(4) evaluates to 5 * 4, since f(5) returns a function which is basically
def g(b):
return 5 * b
One could now do stuff like this
mult_by_5 = f(5)
[mult_by_5(x) for x in range(10)]
Let's be fancy, what about more nested functions?:
def f(a):
def g(b):
def h(c):
return a * b *c
return h
return g
f(2)(3)(4) # 24

Passing a function as an argument vs Calling it inside. Which one is recomended in Python

Lets say I have three functions in one module as defined below:
def add_nums(a, b):
return a + b
def sum_and_square_one(add_nums, a, b):
result = add_nums(a,b)
return result*result
def sum_and_square_two(a, b):
result = add_nums(a,b)
return result*result
Both functions sum_and_square_one and sum_and_square_two do the same task. But the former takes add_nums as an argument while the latter calls add_nums inside. My question is which one is the better way. Passing a function as an argument or calling inside a function?
If you always want to call the same function then there is no point in passing it as an argument every time. (this is the case in your example)
If you, however, want to dynamically call different (maybe similar) functions, then passing the appropiate function as an argument would make sense.
Depends on your use case. Do you need the function to change the way it works depending on that argument?
Or, rewriting your meaningless example into a bit less meaningless example (but still quite):
def add_nums(a, b):
return a + b
def do_something_then_square(a, b, what_to_do):
result = what_to_do(a, b)
return result * result
def sum_then_square(a, b):
result = add_nums(a, b)
return result * result
# Then you do:
sum_then_square(2, 5) # 49
do_something_then_square(2, 5, what_to_do=add_nums) # 49
do_something_then_square(2, 5, what_to_do=lambda a, b: a + b) # 49
do_something_then_square(2, 5, what_to_do=lambda a, b: a * b) # 100
do_something_then_square(2, 5, what_to_do=min) # 4
do_something_then_square(2, 5, what_to_do=complex) # (-21+20j)
Only question is: do you need that added flexibility? Otherwise it's just useless additional typing. That question must be answered on a case by case basis; it needs a complete example to give a useful answer.

Categories

Resources