Setting parameters with decorators vs nested functions - python

I need to call a multi-parameter function many times while all but one parameter is fixed. I was thinking of using decorators:
# V1 - with #decorator
def dec_adder(num):
def wrap(fun):
def wrapped_fun(n1):
return fun(n1, second_num=num)
return wrapped_fun
return wrap
#dec_adder(2)
def adder(first_num, second_num):
return first_num + second_num
print adder(5)
>>> 7
But this seems confusing since it appears to be calling a 2-parameter function, adder with only one argument.
Another approach is to use a nested function definition that uses local variables from the parent function:
# V2 - without #decorator
def add_wrapper(num):
def wrapped_adder(num_2):
return num + num_2
return wrapped_adder
adder = add_wrapper(2)
print adder(5)
>>> 7
But I hesitate to use this approach since in my actual implementation the wrapped function is very complex. My instinct is that it should have a stand-alone definition.
Forgive me if this ventures into the realm of opinion, but is either approach considered better design and/or more Pythonic? Is there some other approach I should consider?

functools.partial should work nicely in this case:
from functools import partial
def adder(n1, n2):
return n1 + n2
adder_2 = partial(adder, 2)
adder_2(5)
Its' docstring:
partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.
-- so, you can set keyword arguments as well.
PS
Sadly, the built-in sum does not suit this case: it sums over an iterable (in fact, sum(iterable[, start]) -> value), so partial(sum, 2) does not work.

Another possible solution - you can use functools and parametrized decorator:
from functools import wraps
def decorator(num):
def decor(f):
#wraps(f)
def wrapper(n,*args,**kwargs):
return f(n+num,*args,**kwargs)
return wrapper
return decor
#decorator(num=2) # number to add to the parameter
def test(n,*args,**kwargs):
print n
test(10) # base amount - prints 12

Related

python decorator doesn't take the arguments of the function

I'm learning python and I'm stuck at decorators
the way I understand it is that decorators add functionality to a function
I made a simple function that checks if a number is even
and then a decorator that adds taking the absolute value to it
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new()
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
is_even(8)
but it I keep getting a TypeError: abs() takes exactly one argument (0 given)
is there is something wrong in the code or is my understanding of decorators is false?
When you decorate the function, you should return the decorated function itself:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new
rather than calling the decorated function and returning that:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
func(num)
return is_even_new()
(notice the extra parentheses). In this last example, when you decorate a function with #decorate, the decorator defines this inner function is_even_new and then, instead of returning the latter, it tries to call it with no arguments: is_even_new(). This is also why you're getting the TypeError: abs expects (exactly) one argument, but you've given it none.
Remember that functions in Python are objects like anything else ("first-class citizens") and so you can reference them directly by their name.
Also, as a recommendation, if you know that the functions decorated with #decorate will only ever take exactly one argument (like is_even), don't use variable arguments and keyword arguments, just define the decorate function to take exactly one parameter as well:
import functools
def decorate(func):
#functools.wraps(func)
def decorated(x):
func(abs(x))
return decorated
This will make the error messages more helpful (instead of raising at the abs call,
Or, if you only want to apply abs to, say, the first argument:
def decorate(func):
#functools.wraps(func)
def decorated(x, *args, **kwargs):
func(abs(x), *args, **kwargs)
return decorated
You'll also have noticed the #functools.wraps. This is a very useful standard library utility for defining wrapper functions, such as those you return from a decorator. It sets special attributes like __name__, __module__, etc. on the wrapper function so that it essentially looks like the wrapped function.
in your code you need to return the value from the decorator in correct way and also change the input data to the decorator by keeping the limitation of other inside function in mind ie (abs take 1 positonal argument not many) and you cant directly call the return function from outside as full without provideing value as return is_new_even(), you need to pass the argument there also if want to do that way
def decorate(func):
def is_even_new(val):
num = abs(val)
return func(num)
return is_even_new
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
is_even(8)
I am not sure what your decorated function is trying to do as modulo is working on negative integers as well, but you basically have two problems:
like #Anakhand said you should return a function and not the function return value.
your is_even_new does not return nothing
so I guess this is the correct implemntation:
def decorate(func):
def is_even_new(*args,**kwargs):
num = abs(*args,**kwargs)
return func(num)
return is_even_new
#decorate
def is_even(x):
if x%2 == 0:
return True
else:
return False
print(is_even(-8)) # --> True

Python currying with any number of variables

I am trying to use currying to make a simple functional add in Python. I found this curry decorator here.
def curry(func):
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= func.__code__.co_argcount:
return func(*args, **kwargs)
return (lambda *args2, **kwargs2:
curried(*(args + args2), **dict(kwargs, **kwargs2)))
return curried
#curry
def foo(a, b, c):
return a + b + c
Now this is great because I can do some simple currying:
>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6
But this only works for exactly three variables. How do I write the function foo so that it can accept any number of variables and still be able to curry the result? I've tried the simple solution of using *args but it didn't work.
Edit: I've looked at the answers but still can't figure out how to write a function that can perform as shown below:
>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6
>>> foo(1)(2)
3
>>> foo(1)(2)(3)(4)
10
Arguably, explicit is better than implicit:
from functools import partial
def example(*args):
print("This is an example function that was passed:", args)
one_bound = partial(example, 1)
two_bound = partial(one_bound, 2)
two_bound(3)
#JohnKugelman explained the design problem with what you're trying to do - a call to the curried function would be ambiguous between "add more curried arguments" and "invoke the logic". The reason this isn't a problem in Haskell (where the concept comes from) is that the language evaluates everything lazily, so there isn't a distinction you can meaningfully make between "a function named x that accepts no arguments and simply returns 3" and "a call to the aforementioned function", or even between those and "the integer 3". Python isn't like that. (You could, for example, use a zero-argument call to signify "invoke the logic now"; but that would break special cases aren't special enough, and require an extra pair of parentheses for simple cases where you don't actually want to do any currying.)
functools.partial is an out-of-box solution for partial application of functions in Python. Unfortunately, repeatedly calling partial to add more "curried" arguments isn't quite as efficient (there will be nested partial objects under the hood). However, it's much more flexible; in particular, you can use it with existing functions that don't have any special decoration.
You can implement the same thing as the functools.partial example for yourself like this:
def curry (prior, *additional):
def curried(*args):
return prior(*(args + additional))
return curried
def add(*args):
return sum(args)
x = curry(add, 3,4,5)
y = curry(b, 100)
print y(200)
# 312
It may be easier to think of curry as a function factory rather than a decorator; technically that's all a decorator does but the decorator usage pattern is static where a factory is something you expect to be invoking as part of a chain of operations.
You can see here that I'm starting with add as an argument to curry and not add(1) or something: the factory signature is <callable>, *<args> . That gets around the problem in the comments to the original post.
FACT 1: It is simply impossible to implement an auto currying function for a variadic function.
FACT 2: You might not be searching for curry, if you want the function that will be passed to it * to know* that its gonna be curried, so as to make it behave differently.
In case what you need is a way to curry a variadic function, you should go with something along these lines below (using your own snipped):
def curryN(arity, func):
"""curries a function with a pre-determined number of arguments"""
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= arity:
return func(*args, **kwargs)
return (lambda *args2, **kwargs2:
curried(*(args + args2), **dict(kwargs, **kwargs2)))
return curried
def curry(func):
"""automatically curries a function"""
return curryN(func.__code__.co_argcount, func);
this way you can do:
def summation(*numbers):
return sum(numbers);
sum_two_numbers = curryN(2, summation)
sum_three_numbers = curryN(3, summation)
increment = curryN(2, summation)(1)
decrement = curryN(2, summation)(-1)
I think this is a decent solution:
from copy import copy
import functools
def curry(function):
def inner(*args, **kwargs):
partial = functools.partial(function, *args, **kwargs)
signature = inspect.signature(partial.func)
try:
signature.bind(*partial.args, **partial.keywords)
except TypeError as e:
return curry(copy(partial))
else:
return partial()
return inner
This just allow you to call functools.partial recursively in an automated way:
def f(x, y, z, info=None):
if info:
print(info, end=": ")
return x + y + z
g = curry_function(f)
print(g)
print(g())
print(g(2))
print(g(2,3))
print(g(2)(3))
print(g(2, 3)(4))
print(g(2)(3)(4))
print(g(2)(3, 4))
print(g(2, info="test A")(3, 4))
print(g(2, info="test A")(3, 4, info="test B"))
Outputs:
<function curry.<locals>.inner at 0x7f6019aa6f28>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a0d0>
9
9
9
test A: 9
test B: 9

Why is there no argument when returning function in a decorator.

Example:
Fibonaci recursive function with memoize decorator. When calling function helper there is no argument. If function helper is defined to take an argument x therefore i expect calling the function with one argument. I would like to understand why is syntax like it is?
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
#memoize
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(40))
You do call the helper with the argument. Decorators are syntactic sugar for this
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
fib = memoize(fib)
So your fib function is no longer the original fib-function. It is in fact the helper closure, because that's what memoize returns – a closure. So, when you call fib(40) you kind of call helper(40). The memoize decorator creates a function object, it doesn't call it, just returns it.
I believe the syntax is like this so that it looks as streamlined as possible. You put a decorator object after the # (a function that takes one argument, and returns a function), and python calls it with the function you are defining. This
#memoize
def fib(n):
...
Is exactly equivalent to the following, which doesn't use the magic decorator syntax:
def fib(n):
...
fib = memoize(fib)
If you want your head to spin a little, consider that # can actually be followed by a function call -- but this function call must return a decorator that works as above! Here's a silly example that counts how many times the decorated function is called, but lets you set the start value. (It's just an example: it's not very useful since only one function can be decorated, etc.)
def countcalls(start):
global _calls
_calls = start
def decorator(f):
def wrapper(x):
global _calls
_calls += 1
return f(x)
return wrapper
return decorator
#countcalls(3)
def say(s):
print(s)
say("hello")
# _calls is now 4
Here, countcalls(4) defines and returns (without calling it) the function decorator, which will wrap the decorated function and return the wrapper in place of the function I wrote.

Return function with function

I would like to do something like the following:
def getFunction(params):
f= lambda x:
do stuff with params and x
return f
I get invalid syntax on this. What is the Pythonic/correct way to do it?
This way I can call f(x) without having to call f(x,params) which is a little more messy IMO.
A lambda expression is a very limited way of creating a function, you can't have multiple lines/expressions (per the tutorial, "They are syntactically restricted to a single expression"). However, you can nest standard function definitions:
def getFunction(params):
def to_return(x):
# do stuff with params and x
return to_return
Functions are first-class objects in Python, so once defined you can pass to_return around exactly as you can with a function created using lambda, and either way they get access to the "closure" variables (see e.g. Why aren't python nested functions called closures?).
It looks like what you're actually trying to do is partial function application, for which functools provides a solution. For example, if you have a function multiply():
def multiply(a, b):
return a * b
... then you can create a double() function1 with one of the arguments pre-filled like this:
from functools import partial
double = partial(multiply, 2)
... which works as expected:
>>> double(7)
14
1 Technically a partial object, not a function, but it behaves in the same way.
You can't have a multiline lambda expression in Python, but you can return a lambda or a full function:
def get_function1(x):
f = lambda y: x + y
return f
def get_function2(x):
def f(y):
return x + y
return f

Professor used "The binary version of a function". Does that even exist?

Our professor used this in the assignment. I don't think "The binary version of a function" exist after searching about it in Google. What do you think it means?
Say we have a function add that adds a bunch of numbers. Rather than
writing add(3, 5, 4, 1) we want to use currying to create an adder
function that can be extended using a chain of calls. We would then
have adder(3)(5)(4)(1)(). Let us assume we have the currying function
that can create this adder given the add2 function (the binary version
of add) and a start value. Let us call it curry. Then we have adder =
curry(add2, 0).
I think he means a function that accepts only two arguments, so it just adds two numbers. His example function add(3, 5, 4, 1) would be a function that accepts any number of arguments and adds them all, but add2 would only accept two arguments, so add2(3, 5) would be 8. "The binary version of a function" in this case means a binary function (a function accepting two arguments).
In this case "binary function" refers to an argument that accepts two arguments. In this case your professor is probably referring to something like this:
def add2(x, y):
return x + y
# equivalently: add2 = lambda x,y: x+y
def curry(func, num):
def wrapped(*args):
if len(args) == 0:
return num
elif len(args) > 1:
raise TypeError('{} takes 1 positional argument but '
'{} were given'.format(
func.__name__, len(args)))
arg = args[0]
return curry(func, func(num, arg))
return wrapped
#AdamSmith and #BrenBarn have already pointed out what binary function means. A simple and clear assignment solution can be write by using object instead of decorator.
class curry():
def __init__(self, func, default):
self._f = func
self._default = default
def __call__(self, val=None):
if val is None:
return self._default
return curry(self._f,self._f(self._default,val))
print(curry(lambda x,y:x+y, 0)(3)(5)(4)(1)())
Neat and simple!
IMHO functors should be used only when the increase readability, simplicity or hide tedious work. In that case the object and functor implementations are really the same but the object version is more readable and straight to understand.

Categories