["python","local-variables"]
Access a function variable outside the function without using "global"

I am trying to access a local function variable outside the function in Python. I can make code like this work with global variables: bye = '' def hi(): global bye bye = 5 sigh = 10 hi() print(bye) Next, I tried this code, hoping to access bye outside hi() without using global bye: def hi(): bye = 5 sigh = 10 return hi() x = hi() print(x.bye) This gives AttributeError: 'NoneType' object has no attribute 'bye'. Next, I tried: def hi(): bye = 5 sigh = 10 return bye hi() x = hi() print(x.bye) This didn't improve matters; I get AttributeError: 'int' object has no attribute 'bye'. Is there a way to access a local function variable (bye) outside its function (hi()) without using globals and also without printing out the sigh variable? How can I do it?
You could do something along these lines (which worked in both Python v2.7.17 and v3.8.1 when I tested it/them): def hi(): # other code... hi.bye = 42 # Create function attribute. sigh = 10 hi() print(hi.bye) # -> 42 Functions are objects in Python and can have arbitrary attributes assigned to them. If you're going to be doing this kind of thing often, you could implement something more generic by creating a function decorator that adds a this argument to each call to the decorated function. This additional argument will give functions a way to reference themselves without needing to explicitly embed (hardcode) their name into the rest of the definition and is similar to the instance argument that class methods automatically receive as their first argument which is usually named self — I picked something different to avoid confusion, but like the self argument, it can be named whatever you wish. Here's an example of that approach: def add_this_arg(func): def wrapped(*args, **kwargs): return func(wrapped, *args, **kwargs) return wrapped @add_this_arg def hi(this, that): # other code... this.bye = 2 * that # Create function attribute. sigh = 10 hi(21) print(hi.bye) # -> 42 Note This doesn't work for class methods. Just use the instance argument, named self by convention, that's already passed to methods instead of the method's name. You can reference class-level attributes through type(self). See Function's attributes when in a class.