0

Some basic question from beginner. Is there a way to "push" attribute to a decorated function not using function arguments ?

import sys
from functools import wraps


def decorator_(func):
    @wraps(func)
    def newfunc():
        func.some_attr = 'some_attr'
        func()
    return newfunc

@decorator_
def decorated_function():
    # ??? access some_attr ???
    print some_attr

def main():
    decorated_function()

if __name__ == '__main__':
    sys.exit(main())

Thanks in advance.

psb
  • 342
  • 3
  • 12
  • I'm not sure it's exactly what you want, but this is similar: http://stackoverflow.com/questions/1965607/how-can-i-pass-a-variable-in-a-decorator-to-functions-argument-in-a-decorated-f It does use arguments, but in a way that is compatible with what you are trying to accomplish, as far as I can tell. – Paulo Almeida Aug 30 '16 at 10:02
  • Thanks Paulo, this is a useful link. – psb Aug 30 '16 at 10:19

2 Answers2

0

Depending on whether you use Python 2 or 3 you can inject variables into the globals of a function like this:

Python 2

func.func_globals["some_attr"] = "some_value"

Python 3

func.__globals__["some_attr"] = "some_value"
filmor
  • 30,840
  • 6
  • 50
  • 48
  • Thanks, this works. And it feels like the solution I was looking for but I'm a bit confused that Antti's solution works too. It looks like two methods to do the same. There are probably some hidden stones somewhere which I can not see ? – psb Aug 30 '16 at 10:13
  • After inspecting Antti's answer and comment - is there any way to inject variable to function "locals" ? That would be preferable way. – psb Aug 30 '16 at 10:44
0

If you set the attribute on new_func instead, you can access it simply as decorated_function.some_attr:

def decorator_(func):
    @wraps(func)
    def newfunc():
        newfunc.some_attr = 'some_attr'
        func()
    return newfunc

@decorator_
def decorated_function():
    print(decorated_function.some_attr)

Otherwise, wraps makes the original function available as decorated_function.__wrapped__ in Python 3:

def decorator_(func):
    @wraps(func)
    def newfunc():
        func.some_attr = 'some_attr'
        func()
    return newfunc

@decorator_
def decorated_function():
    print(decorated_function.__wrapped__.some_attr)

In Python 2, the __wrapped__ is not set by wraps, so we need to set it up manually:

def decorator_(func):
    @wraps(func)
    def newfunc():
        func.some_attr = 'some_attr'
        func()
    newfunc.__wraps__ = func
    return newfunc

However, this sounds like an XY problem; if you want to pass a value to the decorated_function you should let decorator_ pass it as an argument instead.

  • Thanks, this works. It's a bit off what I was expecting in a way that I have to refer to attribute as func_name.attr instead of just attr. But I'm a bit confused that filmor's solution works too. It looks like two methods to do the same. There are probably some hidden stones somewhere which I can not see ? – psb Aug 30 '16 at 10:14
  • @psb actually you'd want to do **neither** of these. filmor's solution sets a *global variable* in the module that defined `decorated_function` – Antti Haapala -- Слава Україні Aug 30 '16 at 10:18
  • Could you please point me to some reasons why ? In a some very common words. – psb Aug 30 '16 at 10:21
  • I'm very sorry, but I was too quick to say that your answer works for me. Looks like your answer works in Python3 only ? – psb Aug 30 '16 at 10:40
  • @psb ah, it is because `wraps` doesn't set `__wrapped__` in Python 2. – Antti Haapala -- Слава Україні Aug 30 '16 at 11:37