I tried to use function decorators, but in this example it dooesn't work for me, can you give me the solution ?
def multiply_by_three(f):
def decorator():
return f() * 3
return decorator
#multiply_by_three
def add(a, b):
return a + b
print(add(1,2)) # returns (1 + 2) * 3 = 9
Interpreter prints error: "TypeError: decorator() takes 0 positional arguments but 2 were given"
When you use a decorator, the function you return from the decorator replaces the old function. In other words, the decorator function in multiply_by_three replaces the add function.
This means that each functions signature's should match, including their arguments. However, in your code add takes two arguments while decorator takes none. You need to let decorator receive two arguments as well. You can do this easily by using *args and **kwargs:
def multiply_by_three(f):
def decorator(*args, **kwargs):
return f(*args, **kwargs) * 3
return decorator
If you now decorate your function and run it, you can see it works:
#multiply_by_three
def add(a, b):
return a + b
print(add(1,2)) # 9
Related
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
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
I have a function -
def add(a, b):
return a+b
Now I want it to add 100 to the result using a decorator such that -
#decorator(100)
def add(a, b):
return (a+b)
class decorator():
def __init__(self, c):
self.c = c
def __call__(self, f):
def _wrapped_f(a, b):
return self.c + f(a, b)
return _wrapped_f
The problem is when I want to keep the extra argument variable and not fixed to 100.
Something like -
#decorator(c)
def add(a, b):
return (a+b)
Can I somehow assign the value of this variable during the function call. (when add function is called)
The reason I want to do this using a decorator is because I don't want to modify my function add.
I cannot afford an extra parameter in function something like -
def(a, b, c=0):
return (a+b+c)
Hence, the need of the decorator.
Sure. All a decorator does is replace the original function with a wrapped version; that wrapper can take as many parameters as you like.
I'm not sure why you are using a class - this would be clearer as a standard decorator.
def decorator(func):
def wrapped(a, b, c):
return c + func(a, b)
return wrapped
#decorator
def add(a, b):
return (a+b)
Not 100% sure what you want to do with this decorator, so this might not be exactly what you want, but even then, others might find it useful.
You can add another parameter to the function, just by adding more parameters to the _wrapped function, or even by passing *args and **kwargs to that function. But how do you handle those parameters? Surely, just adding those parameters to the result only makes sense for the add function.
In the general case, you could use the decorated function itself to process the additional parameters. Then, just have the decorated function reduce all the parameters using the original function:
from functools import reduce # Python 3
def vararg(f):
""" change two-arg-function into vararg-function"""
def _f(*args):
return reduce(f, args)
return _f
#vararg
def add(a, b):
return a + b
print(add(1, 2)) # -> 3
print(add(1, 2, 3)) # -> 6
print(add(1, 2, 3, 4)) # -> 10
print(add(1, 2, 3, 4, 5)) # -> 15
I am using a single decorator for two separate functions: one with specification of a decorator argument; and another one without it.
When the optional argument is not passed, the return type is a function (specifically, the inner_function in the decorator). However, when the optional argument is passed it works as expected.
Can you explain what is going on here and why it acts differently in these cases?
def cache_disk(cache_folder="./cache"):
def wrapper(f):
def inner_function(*args, **kwargs):
result = f(*args, **kwargs)
return result
return inner_function
return wrapper
#cache_disk
def func1(data):
return [d for d in data]
#cache_disk(cache_folder='./cache/')
def func2(data):
return [d for d in data]
data = [1,2,3]
print(func1(data))
print(func2(data))
Result:
<function inner_function at 0x7f1f283d5c08>
[1, 2, 3]
Note that:
#decorator # no arguments
def func(...):
...
is equivalent to:
def func(...):
...
func = decorator(func) # one 'level' of calls
and that:
#decorator(...): # arguments
def func(...):
...
is equivalent to:
def func(...):
...
func = decorator(...)(func) # two 'levels' of calls
In the first case, there is a single argument to the decorator, the func itself. In the second case, the arguments to the decorator are the ... from the # line, and it's the function returned by the decorator that is called with func as an argument.
In your example,
#cache_disk
def func1(data):
...
the decorator cache_disk gets a single, callable argument (func, which becomes args[0]) and returns the wrapper. Then when you call:
print(func1(data))
wrapper gets a single argument (data, which becomes f) and returns inner_function.
Therefore, you have three choices:
Decorate func1 with #cache_disk() (note parentheses), passing no arguments to cache_disk itself and func to wrapper;
Alter cache_disk to behave differently depending on whether it's passed a single, callable argument or something else; or
As #o11c pointed out in the comments, use e.g. cache_disk.wrapper = cache_disk() to provide a convenient alias for the parameter-less version, then decorate with #cache_disk.wrapper.
if you want default values, you need to call the function, which returns a decorator:
#cache_disk()
def func1(data):
return [d for d in data]
Suppose I have a function which I have written as a binary-operator (binop), how do I extend it to a multi-operator (multiop) which takes an arbitrary number of arguments? Is there such a decorator in a library (e.g. in functools)?
For example (I want a decorator to give this behaviour):
#binop_to_multiop
def mult(a,b):
return a*b
mult(2,3,4) # 2*3*4 = 24
mult(7) # 7
mult(2,3) # 6
Obviously I can't ask a question about decorators without mentioning, this answer.
.
I've tried writing my own, but can't quite get it working, any explanation of where I'm going wrong would also be welcome:
def binop_to_multiop(f):
#functools.wraps(f)
def wrapper(*args, **kwds):
if len(args) == 1: return args[0] # fails
return f(args[0],(f(*args[1:], **kwds)), **kwds) #recursion attempt fails
return wrapper
Gives a TypeError: mult() takes exactly 2 arguments (N given) (for various N!=2).
reduce() comes to mind:
from functools import wraps
def binop_to_multiop(binop):
#wraps(binop)
def multiop(x, *xs):
return reduce(binop, xs, x)
return multiop
# ...
#binop_to_multiop
def mult(a, b):
return a * b
print mult(2, 3, 4)
print mult(7)
print mult(2, 3)
Result:
$ python multiop.py
24
7
6
Your attempt to code it yourself was very close to working. You just need to change the recursive step to recurse on wrapper rather passing all but one argument to f:
def binop_to_multiop(f):
#functools.wraps(f)
def wrapper(*args, **kwds):
if len(args) == 1: return args[0]
return f(args[0], wrapper(*args[1:], **kwds), **kwds)
return wrapper
I didn't have any problems with the base case, so I'm not sure what your comment #fails was about.
You may also need to think about which end of the list you start solving from (that is, does your operator have left or right associativity). For operators like multiplication and addition it won't matter since (a+b)+c = a+(b+c), but for other you may get strange results. For instance, subtraction may not work as you might expect:
#binop_to_multiop
def sub(a, b):
return a - b
With the decorator defined above, sub(a, b, c) will give a different result than a-b-c (it will do a-(b-c) instead of (a-b)-c). If you want them to behave the same way, you can redefine the decorator to be left associative (like most mathematical operators do in most computer languages) like this:
def left_associative_binop_to_multiop(f):
#functools.wraps(f)
def wrapper(*args, **kwds):
if len(args) == 1: return args[0]
return f(wrapper(*args[:-1], **kwds), args[-1], **kwds)
return wrapper
A more sophisticated approach would be to make the associativity be a parameter to the decorator, but that gets tricky if you don't want the parameter to be required.
def binop_to_multiop(f):
def wrapper(*args):
return reduce(f, args) if args else None
return wrapper
#binop_to_multiop
def mult(a, b):
return a*b
print mult(2,3,4)
print mult(7)
print mult(2,3)
print mult(4,5,6,7)
gives
24
7
6
840