Nisali Kularatne
Nisali Kularatne

Reputation: 39

decorators execution of python

registry = []
def register(func):
 print('running register(%s)' % func)
 registry.append(func)
 return func

@register
def f1():
 print('running f1()')
@register
def f2():
 print('running f2()')
def f3():
 print('running f3()')
def main():
  print('running main()')
  print('registry ->', registry)
  f1()
  f2()
  f3()
if __name__=='__main__':
  main()

in the above program why doesnt the f1() f2() and f3() in main work if return func is not used in the register function

Upvotes: 0

Views: 56

Answers (1)

John Coleman
John Coleman

Reputation: 51998

A decorator is a higher-order function which takes a function as input and returns a function as output. Decorator syntax is a way of calling this higher order function.

The lines

@register
def f1():
 print('running f1()')

are equivalent to the lines

def f1():
 print('running f1()')

f1 = register(f1)

Usually, the decorator modifies the decorated function. In this case it is returned unmodified. But -- it is still returned. If the decorator returned nothing then the decorator would replace f1 by None (which wouldn't be very useful). This particular decorator is called for its side effects (registering a function). The fact that it returns an unmodified function doesn't mean that it isn't called.

The output that you see should make sense now:

running register(<function f1 at 0x0000000001DCCBF8>)
running register(<function f2 at 0x0000000003566D90>)
running main()
registry -> [<function f1 at 0x0000000001DCCBF8>, <function f2 at 0x0000000003566D90>]
running f1()
running f2()
running f3()

You used @register twice, which calls register() twice, before main is run. Neither f1 nor f2 were actually changed by the decorator, hence they work as expected when called in main(). The printed value of registry shows that the decorator worked as intended: it registered the decorated f1, f2 but didn't register the undecorated f3.

On the other hand, if you remove the line return func from the decorator, you see (something like):

running register(<function f1 at 0x00000000004DCBF8>)
running register(<function f2 at 0x0000000003586D90>)
running main()
registry -> [<function f1 at 0x00000000004DCBF8>, <function f2 at 0x0000000003586D90>]
Traceback (most recent call last):
  File "C:\Users\jcoleman\Documents\Programs\PythonScripts\socode.py", line 68, in <module>
    main()
  File "C:\Users\jcoleman\Documents\Programs\PythonScripts\socode.py", line 64, in main
    f1()
TypeError: 'NoneType' object is not callable

The decorator is still called twice -- but they are now (implicitly) returning None. As soon as f1() is hit in main you get a run-time error since at that stage f1 is None, which isn't callable.

See this question for more on decorators.

Upvotes: 1

Related Questions