简体   繁体   中英

How instance attributes are passed to the decorator inner function?

I recently studied how decorators work in python, and found an example which integrates decorators with nested functions. The code is here :

def integer_check(method):
    def inner(ref):
        if not isinstance(ref._val1, int) or not isinstance(ref._val2, int):
            raise TypeError('val1 and val2 must be integers')
        else:
            return method(ref)
    return inner


class NumericalOps(object):
    def __init__(self, val1, val2):
        self._val1 = val1
        self._val2 = val2

    @integer_check
    def multiply_together(self):
        return self._val1 * self._val2

    def power(self, exponent):
        return self.multiply_together() ** exponent

y = NumericalOps(1, 2)

print(y.multiply_together())
print(y.power(3))

My question is how the inner function argument("ref") accesses the instance attributes (ref._val1 and ref._val2)? It seems like ref equals the instance but i have no idea how it happenes.

well one explanation I found some time ago about the self argument was that this:

y.multiply_together()

is roughly the same as

NumericalOps.multiply_together(y)

So now that you use that decorator it returns the function inner which requires the ref argument so I see that roughly happen like this (on a lower level):

NumericalOps.inner(y)

Because inner "substitutes" multiply_together while also adding the extra functionality

Let's first recall how a decorator works:

Decorating the method multiply_together with the decorator @integer_check is equivalent to adding the line: multiply_together = integer_check(multiply_together) , and by the definition of multiply_together , this is equivalent to multiply_together = inner .

Now, when you call the method multiply_together , since this is an instance method, Python implicitly adds the class instance used to invoke the method as its first (an only, in this case) argument. But multiply_togethet is, actually, inner , so, in fact, inner is invoked with the class instance as an argument. This instance is mapped to the parameter ref , and through this parameter the function gets access to the required instance attributes.

inner replaces the original function as the value of the class attribute.

@integer_check
def multiply_together(self):
    return self._val1 * self._val2

# def multiply_together(self):
#     ...
#
# multiply_together = integer_check(multiply_together)

first defines a function and binds it to the name multiply_together . That function is then passed as the argument to integer_check , and then the return value of integer_check is bound to the name multiply_together . The original function is now only refernced by the name ref that is local to inner / multiply_together .

The definition of inner implies that integer_check can only be applied to functions whose first argument will have attributes named _val1 and _val2 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM