codyc4321
codyc4321

Reputation: 9682

what is function(var1)(var2) in python

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

Answers (3)

Two-Bit Alchemist
Two-Bit Alchemist

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:

  • The function 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

bagrat
bagrat

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:

  1. First the 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 returned
  2. Whatever is returned from the wrap 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).
  3. Our 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

Peter Rowell
Peter Rowell

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

Related Questions