Python inner functions/decorators: When should I use parentheses when returning an inner function?

Question:

I am learning about Python decorators and inner functions and have some questions about the lesson I’m learning via a YouTube video from codeacademy.com https://youtu.be/WOHsHaaJ8VQ.

When using inner functions sometimes I have to return the function with parenthesis, and sometimes without.

If I call an inner function without using decorators, I have to use parentheses when returning the inner function, otherwise it seems the inner function is returned as an object(?).
In the YouTube video from codeacademy.com as well as this one https://www.youtube.com/watch?v=FsAPt_9Bf3U, they call the inner function without parentheses and the expected result is output.

If I call an inner function using decorators, I have to not use parentheses when returning the inner function, otherwise it seems to work correctly, but throws an error along with some other weird results.

I’ve written some code to test different variations and output the results.
You can see the live code here: https://trinket.io/python/af1b47658f

# Test 1: The title function returns inner function wrapper without parentheses.
def title(print_name_function):
  def wrapper():
    print("Professor:")
    print_name_function()
  return wrapper # Without parentheses

def print_my_name():
  print("John")

print('Test 1')
title(print_my_name)
# Results: Nothing is printed.


# Test 2: The title function returns inner function wrapper with parentheses.
def title(print_name_function):
  def wrapper():
    print("Professor:")
    print_name_function()
  return wrapper() # With parentheses

def print_my_name():
  print("John")

print('Test 2')
title(print_my_name)
# Results: Professor John is printed.


# Test 3: Using a decorator while the title function returns inner function wrapper without parentheses
def title(print_name_function):
  def wrapper():
    print("Professor:")
    print_name_function()
  return wrapper # Without parentheses

@title
def print_my_name():
  print("John")

print('Test 3')
print_my_name()
# Results: Professor John is printed.


# Test 4: Using a decorator while the title function returns inner function wrapper with parentheses
def title(print_name_function):
  def wrapper():
    print("Professor:")
    print_name_function()
  return wrapper() # With parentheses

@title
def print_my_name():
  print("John")

print('Test 4')
print_my_name()
# Results: Professor John is printed and the following error is thrown:
'''
Traceback (most recent call last):
  File "decorators.py", line 59, in <module>
    print_my_name()
TypeError: 'NoneType' object is not callable.
'''
# Additionally, Professor John is printed before 'Test 4' is printed which seems that print_my_name() runs, then print('Test 4') runs.

In the two videos I’ve watched listed above about inner functions/decorators I’ve found…

For inner functions: the inner function was returned without using parentheses and ran correctly. Upon my testing, I have to use the parentheses for it to run correctly.

For decorators: the inner function was returned without using parentheses and ran correctly. Upon my testing, running without using parentheses works. Running with parentheses seems to work, but the output order is mixed up and an error is thrown (see test 4 in my code).

Asked By: Josh

||

Answers:

Let’s break this down into two parts.

1) Let’s ignore decorators for now.

You should use parentheses when you want to call some function.

Without parentheses, a function is just its name.

For example:

Here is a function, where we give it a number, and we get back that number plus 5.

def add_five(x):
    return x + 5

We see that add_five, without parentheses, is just the function definition. Think of it as a recipe. It’s not the actually cake, just the instructions on how to bake a cake.

>>> add_five
<function add_five at 0x10da3ce18>

Now we give it an ingredient, and it makes a cake:

>>> add_five(1) 
6

Let’s do a similar thing, but with better names.

>>> def make_cake(cake_type):
>>>     print("Making: " + cake_type + " cake!")

>>> make_cake("carrot")
'Making: carrot cake!'

>>> make_cake
<function make_cake at 0x10da3cf28>

Ok, so when we put the function name without any parentheses, we’re not actually calling the function, we’re just getting the declaration of the function (which is kinda like the function’s Birth Certificate, which has its memory address, in this case: 0x10da3cf28.

The same thing applies for functions that don’t expect any parameters.

  • Without the parentheses, you’re just asking, “Hey function, you exist?”

  • With the parentheses (and necessary parameters/variables required), you’re saying, “Hey function, do something!”

Now for the second part.

2) What do Decorators do?

@SyntaxVoid has a great explanation about what you’re doing. Decorators are a much more complicated thing, so I’ll stick to explaining what they’re doing in this specific context.

Basically, your decorator, @<Some Function Name> specifies a function to call the decorated function on.

def some_decorator(function_that_I_decorated):
    print("I'm going to print this, and then call my decorated function!")
    function_that_I_decorated()

@some_decorator
def my_decorated_function():
    print("Did I do anything?")

Then we see the results:

>>> def some_decorator(function_that_I_decorated):
...     print("I'm going to print this, and then call my decorated function!")
...     function_that_I_decorated()
... 
>>> @some_decorator
... def my_decorated_function():
...     print("Did I do anything?")
... 
I'm going to print this, and then call my decorated function!
Did I do anything?

Now here’s the important part:

>>> my_decorated_function
>>> my_decorated_function() 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Wait… didn’t we define my_decorated_function?

Yes, we defined the function, but the decorator is reassigning that function name to something else.

Namely, my_decorator_function = some_decorator(my_decorator_function)

Now some_decorator happens to do something before calling my_decorator_function. It prints some stuff. But what is the return value of some_decorator? There’s no return statement, so some_decorator returns None by default.

Therefore, my_decorator_function was created, run, and now has a new value.

Why would we want this behavior?

When we want the output to change, when running the same function with the same input(s) multiple times.

For example, maybe I want a function that returns “Go Left” every other time it’s called, or “Go Right” every 5-times the function gets called.

If I want to do this with a function with more than one variable, that’s easy! Just pass it in and check if num_times == whatever_int.

But life ain’t so easy- sometimes other people already have functions written that are much simpler and only allow one variable, because that’s more generalizable. Or maybe it’s so complex it would take us a really long time to figure out how the function works (and we usually don’t want to violate abstraction barriers anyways). In those situations, we need to adapt their function to our needs.

I would encourage you to read more about Currying, as that’ll help you understand other uses too.

Answered By: Dave Liu

Let me use this famous quote first.

In python everything is an object.

I’ve been wrapping my head about two hours, until I remembered this quote. We should think original function and decorated function as objects, given an example:

def decorator(original_func):
    def decorated_func():
        print("Start decoration")
        original_func()
        print("End decoration")
    return decorated_func # return decorated function as object without calling

@decorator
def func():
    print("I will be decorated")

func()

The decorator_func transfers the func object to decorated_func object, and return the decorated_func as an object, so when we call func object with it’s original name, we are actually calling the new function object decorated_func, which is equivalent to decorated_func().

Now it is easy to see why return decorated_func() is wrong, if we return decorated_func() in definition of decorator, we are return None, because the default return value of a function is None, and None is not callable as traceback says, so we can’t use func() to call func.

Additionally, although the following two codes are equivalent, decorator help us to simplify our code, and without changing the original function object, also don’t need to create a new function object mannually

@decorator
def func():
    print("I will be decorated")

func()
def func():
    print("I will be decorated")

dec_func = decorator(func)
dec_func()
Answered By: Broken Dreams
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.