how a function in python is getting called by just

2019-07-26 17:15发布

问题:

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

回答1:

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:

  1. 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.
  2. 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).
  3. 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.
  4. 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.



回答2:

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.