Search code examples
pythonpython-2.7decoratorpython-decorators

Decorators not changing dir()


Python decorators when used without

@functools.wraps(function)

changes the original function's __doc__,__name__,help which is understandable as decorator is simply

fun=decorate(fun)

but why doesnt it change dir(fun) for that function ? shouldn't it change as well ?

For example:

def wow(func):
    def __wow():
        return func()
    return __wow

@wow
def fun():
    print "yes"
fun.b=1
print fun.__name__
print dir(fun)

The output is

__wow   => name of decorator
[...   '__sizeof__', '__str__', '__subclasshook__', 'b', 'func_closure', ...]
                                                    ^^

If you see here,when we print name it is of decorator but when we print dir its of fun as b is in there.So why is b there?Shouldnt it be dir(decorator) as fun.name gives that of decorator


Solution

  • You seem to miss a crucial step in decorators. You do grasp that using a decorator @decorate on a function fun does this:

    fun = decorate(fun)
    

    but then don't follow through. fun is now bound to the return value of the decorator.

    You said:

    Python decorators [...] changes the original function's __doc__, __name__, help

    This is not true. The original, undecorated function is unchanged. It still has the same __doc__, the same __name__, etc. The returned object from the decorator, now bound to the original name, however, may well have a different name or docstring. After all, it may well be a different object altogether.

    In your sample, you returned __wow from the decorator, so Python assigned that object to fun. fun is just a name here, one that references the result of the decorator. The original fun function is no longer available via that name; at no point are you inspecting the original, undecorated function here.

    So using dir() on the name fun means it is applied to whatever fun is bound to now, and that is the __wow function produced by the decorator. Setting attributes on fun, sets attributes on that same function object. All attributes reflect this.

    You may find it helpful to step through what Python does and visualise those steps. See this Python Tutor visualisation; it shows you what fun references in the end (and where the original function object ended up):

    references in the Python Tutor visualisation