First of all to find "lcm" of two numbers I made a function lcm(a, b)
. Then I thought of finding "hcf" too so I made a decorator decor
and defined a function hcf(a, b)
in it. And then I returned this function by just typing the name of the function and I didn't put brackets with it but it is still working. I cant understand why this function is working even though I didn't used brackets.
def decor(lcm_arg): # just to practice decorators
def hcf(a, b):
if a > b:
a, b = b, a
while True:
if b % a == 0:
print("hcf is", a)
break
else:
a, b = b % a, a
return lcm_arg(a, b)
return hcf # how hcf function is working without using brackets
@decor
def lcm(a, b):
if a > b:
a, b = b, a
for x in range(b, a*b+1, b):
if x % a == 0:
print("lcm is", x)
break
lcm(2, 4)
Output:
hcf is 2
lcm is 4
I don't think you understand decorators. Let's make a minimal example.
def my_decorator(some_function):
def new_function(*args, **kwargs):
'announces the result of some_function, returns None'
result = some_function(*args, **kwargs)
print('{} produced {}'.format(some_function.__name__, result))
return new_function # NO FUNCTION CALL HERE!
@my_decorator
def my_function(a, b):
return a + b
my_function(1, 2) # will print "my_function produced 3"
We have a simple function my_function
which returns the sum of its two arguments and a decorator which will just print out the result of whatever function it decorates.
Note that
@my_decorator
def my_function(a, b):
return a + b
is equivalent to
def my_function(a, b):
return a + b
my_function = my_decorator(my_function)
Since my_decorator
accepts a function as an argument (here we are giving it my_function
) and returns a new function new_function
(without calling it!), we effectively override my_function
because we reassign the name to whatever my_decorator
returns.
In action:
>>> my_function(1, 2)
my_function produced 3
Note that at every point in the example when a function is called, it happens with the parentheses-syntax. Here are all the function calls that happen in the first block of code I posted, in order:
my_decorator(my_function)
is called and the return value is reassigned to the name my_function
. This either happens through the @
syntax or more explicitly in the equivalent code snippet.
my_function(1, 2)
is called. At this point, my_function
is the new_function
that got returned by the decorator. Brain-parse it as new_function(1, 2)
.
- Inside the body of
new_function
, the argument we gave to my_decorator
is called (result = some_function(*args, **kwargs)
) which happens to be the value of my_function
before the reassignment that happened in step 1.
print
is called.
If you want to understand how new_function
is holding on to some_function
despite my_decorator
already having returned from its call, I suggest looking into the topics free variables and closures.
return hcf
does not call the function because there are no parentheses, as you noticed. The decor
function is used as a decorator which reassigns the name lcm
to refer to the returned function. What I mean by this is that
@decor
def lcm(a, b):
// ...
is equivalent to
def lcm(a, b):
// ...
lcm = decor(lcm)
After this executes, lcm
refers to the function hcf
. So calling lcm(2, 4)
now executes the code in hcf
. I think the key here is to understand that at the line lcm(2, 4)
, lcm
and hcf
are two names which refer to the same function.