Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3.5 Decorators and function fields

I have the following snippet of code:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        return func(*args, **kwargs)
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

Could you please explain to me why running f_out() raises:

AttributeError: 'function' object has no attribute 'var'

EDIT

I had to elaborate, as an answer has given me the alternative but this will not work for the situation I want it. Given the following snippet:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1
f_out()
print(f_out.var)

I get as output:

0
1

Why is this happening?

like image 732
Vasilis Lemonidis Avatar asked Mar 04 '23 12:03

Vasilis Lemonidis


1 Answers

The correct way is to return the wrapped function and change it before returning it:

def wrapper(func):
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs)
    wrapped.var = 0
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

You correctly get:

print(f_out())

gives

0

The updated snipped changes the var attribute twice:

  • first time in the wrapper where it sets the attribute for the original function to 0 and prints it after calling the original function
  • then when the original function is called from the wrapper, it sets the attribute for the function referenced as f_out to 1. But at that moment, the function referenced as f_out is the wrapped function and no longer the original one.

So when you later print f_out.var you print the attribute for the wrapped function which is 1.

Here is a slightly modified code demonstrating it:

def wrapper(func):
    def wrapped(*args, **kwargs):
        wrapped.orig = func          # keeps a ref to the original function
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1

f_out()
print(f_out.var, f_out.orig.var)

It prints

0
1 0
like image 143
Serge Ballesta Avatar answered Mar 09 '23 00:03

Serge Ballesta