Python - Decorators

2019-01-18 20:09发布

问题:

I'm trying to learn Decorators . I understood the concept of it and now trying to implement it.

Here is the code that I've written The code is self-explanatory. It just checks whether the argument passed in int or not.

def wrapper(func):
    def inner():
        if issubclass(x,int): pass
        else: return 'invalid values'

    return inner()

@wrapper
def add(x,y):
    return x+y

print add('a',2)

It's throwing error saying global name 'x' is not defined. I understand that it is not defined under inner, but didnt know how to rectify this code? Where I'm going wrong?

回答1:

Your decorator should look like:

def wrapper(func):
    def inner(x, y): # inner function needs parameters
        if issubclass(type(x), int): # maybe you looked for isinstance?
            return func(x, y) # call the wrapped function
        else: 
            return 'invalid values'
    return inner # return the inner function (don't call it)

Some points:

  • issubclass expects a class as first argument (you could replace it with a simple try/except TypeError).
  • the wrapper should return a function, not the result of a called function
  • you should actually call the wrapped function in the inner function
  • your inner function didn't had parameters

You can find a good explanation of decorators here.



回答2:

There are three issues I see with your current code.

First, you're calling the inner function, rather than returning a reference to it.

Second, your inner function doesn't take the same arguments as the function you're decorating. In this case, you need to take at least the x argument explicitly (some inner functions can use *args and **kwargs exclusively, but aparently not yours).

Lastly, you're never calling the wrapped function. While this isn't strictly required (it might be useful to swap out a method with a decorator during development), usually you want to call the function at some point during the inner function's code.

So, to wrap the whole thing together, I think you want your code to be something like this:

def wrapper(func):
    def inner(x, y):
        if issubclass(x, int): # issue 2
            return func(x, y) # issue 3
        else:
            return "invalid values" # consider raising an exception here instead!

    return inner # issue 1


回答3:

You may also raise an exception if you would like to terminate the add method properly if type check fails. like this

def check_int_types(func):
    def type_checker(x, y):
        if issubclass(type(x), int) and issubclass(type(y), int):
            return func(x, y)
        raise Exception("Invalid types: {}, {}".format(x, y))
    return type_checker

@check_int_types
def add(a, b):
    return a + b

def main():
    x = y = 15.0
    print add(x, y)

if __name__ == '__main__':
    main()

Result:

Traceback (most recent call last):
  File "deco_test.py", line 17, in <module>
    main()
  File "deco_test.py", line 14, in main
    print add(x, y)
  File "deco_test.py", line 5, in type_checker
    raise Exception("Invalid types: {}, {}".format(x, y))
Exception: Invalid types: 15.0, 15.0


回答4:

this might work.

def wrapper(func):
    def inner(*args,**kwargs):
        if ((args[0] is int) and (args[1] is int)): pass
        else: return 'invalid values'
    return inner
@wrapper
def add(x,y):
    return x+y
print add('a',2)


回答5:

What about this .

def wrapper(func):
    def inner():
        if isinstance(func,int):
                return func(x, y)
        else: return 'invalid values'

    return inner()