Reputation: 9682
I have a piece of code I pulled from someone I don't understand:
def __init__(self, func):
self.func = func
wraps(func)(self)
I've seen things like wraps(func)(self)
several times but never seen it explained. How is there a function with parameters and then another (var)
thing after it? What does it all mean? Thank you
Upvotes: 4
Views: 1731
Reputation: 18467
Functions are first-class objects in Python.
You have no doubt encountered this on the command line if you have typed the name only of a function without parentheses.
In [2]: a
Out[2]: <function __main__.a>
When you see a(b)(c)
it is a method of chaining:
a
is defined to return another function.a(b)
calls a
and returns that reference (to a callable function)a(b)(c)
calls whatever function was returned by a
with c
as an argument.This is equivalent to the following:
new_func = a(b)
new_func(c)
An example:
In [1]: def multiply_by(x):
...: def multiply_by_x(y):
...: return x * y
...: return multiply_by_x # notice no parens
...:
In [2]: multiply_by(10)
Out[2]: <function __main__.multiply_by_x>
Notice when you call this you get a function object. (This is what I mean when I am saying a "reference to a function" or the like.)
In [3]: multiply_by(10)(5)
Out[3]: 50
You're calling the function returned by multiply_by()
with 5 as an argument, and it's exactly the same as doing:
In [4]: multiply_by_10 = multiply_by(10)
In [5]: multiply_by_10(4)
Out[5]: 40
In [6]: multiply_by_10(8)
Out[6]: 80
The cool thing about doing this, as you can see from this example, is that now your multiply_by
function is a factory for functions that multiply by something. Above, we created multiply_by_10
which, obviously, multiplies what you feed it by 10. We can just as easily do:
In [7]: multiply_by_5 = multiply_by(5)
and have a function that multiplies by 5. This is obviously extremely useful. Incidentally, it's also how Python's decorators work.
Thanks @MarkusMeskanen in the comments for pointing out a way to make my silly example into a cooler one!
See also:
Upvotes: 16
Reputation: 7458
You case is that the function wrap
returns a callable, i.e. a function or a class or any object that implements the __call__
method. So just to understand take a look at this example:
def wraps(arg):
def decor(func):
print(arg)
return func
return decor
This is a typical decorator, meaning that you can do the following:
@wrap('some arg')
def func(a):
print(a)
So what happens here is the following:
wrap
function is being called with the argument value some arg
. As you can the inside wrap we have another decor
function defined, which is being returnedwrap
function, is being called, i.e. the __call__
method is being invoked on the returned object, with the argument being the function being decorated. In this case we have decor(func)
.decor
function simply prints the value of arg
('some arg'
in our case), and return the func
.Note: This is being done only once!
func('hello1')
func('hello2')
...you will get the following output
some arg
hello1
hello2
Upvotes: 2
Reputation: 17713
The key is that wraps()
takes a function as its parameter and returns a function, probably with some extra goodness added to it, sort of like what a decorator function does.
If you were to print(wraps(func))
you should see output that identifies the object as a function.
So wraps(func)(self)
can be rewritten as
wrapped_func = wraps(func)
wrapped_func(self)
which looks more "normal" to some people.
Upvotes: 3