78

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?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
askance
  • 1,077
  • 4
  • 14
  • 19
  • 6
    `bye = hi();print bye` – Ashwini Chaudhary Oct 11 '13 at 19:45
  • 2
    What are you trying to achieve? What is the use case? – Rod Oct 11 '13 at 19:50
  • Thanks @hcwhsa ! This works. A follow-up question. Suppose the function defines more than 1 variable but I want to print just the `bye` variable. This solution, however, would print both. I'll edit my question accordingly. – askance Oct 11 '13 at 19:53
  • @Rod - My use case involves using the local variable outside the function. At the same time, the function has multiple variables and so, I have included the `sigh` variable. – askance Oct 11 '13 at 20:01
  • 3
    Are you really just interested in learning how to use `return` or something? – binki Jan 11 '17 at 17:44
  • @binki - Maybe the question implies it and if so , i still need to learn more about the return fn. could you pls clarify how the question connects with the properties of the rtn fn? Thx , – askance Jan 16 '17 at 14:14
  • 2
    If you are using the function to calculate a value and then using that value from outside of the function, then you might as well just directly return the calculated value. Based on the code you’ve shown, you don’t need static variables and you don’t need to access the value through the function itself. You’re just trying to figure out how to give the caller the value you calculated. Returning values is what [`return`](https://docs.python.org/3/reference/simple_stmts.html#return) does. See [this gist](https://gist.github.com/binki/cebd6e5305aa799b386a27c4e12a9873) and let me know. – binki Jan 16 '17 at 16:43

6 Answers6

142

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.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • 16
    You're welcome, but I must add that this practice is fairly unusual. may be hard to debug, and makes the code hard to maintain -- because essentially it's the same thing as using global variables, and which have long [been considered harmful](http://c2.com/cgi/wiki?GlobalVariablesConsideredHarmful). – martineau Oct 12 '13 at 02:11
  • This do not work if you use it in this way `if hi(): print hi.bye`. Anyone know why? – m3nda Dec 06 '15 at 19:00
  • 2
    @erm3nda: As defined, the `hi()` function has no return value, so it effectively returns `None` which is considered a `False` value, so the `if` doesn't execute the conditional `print` statement. – martineau Dec 06 '15 at 19:27
  • @ReeshabhRanjan: Yes it does. The `print hi.bye` needs to be changed to `print(hi.bye)` because of the differences between Python 2 and 3—see [**Print Is A Function**](https://docs.python.org/3.0/whatsnew/3.0.html#print-is-a-function) in the [**_What’s New In Python 3.0_**](https://docs.python.org/3.0/whatsnew/3.0.html) document. Since the newer syntax would work in both versions in this particular case, I'll update my answer to avoid confusing folks... – martineau Sep 10 '17 at 15:19
  • @martineau I tried it but it didn't work. Will try again and update you. – MrObjectOriented Sep 10 '17 at 17:35
  • @martineau I am extremely sorry, I forgot. Well, this doesn't work in Python 3.6 atleast: https://codepad.co/snippet/UPYhKngG – MrObjectOriented Nov 08 '17 at 17:58
  • 1
    @ReeshabhRanjan: As I said before, it **does** work, at least for me anyway, using Python 3.6.3 (and there's really no reason why it wouldn't)—I just ran it again to make sure. If it doesn't work for you, then there must be something else wrong. Note that you have to call the function at least once before trying to access the attribute so it will be created. – martineau Nov 08 '17 at 18:21
  • 1
    This goes way outside of what OP appears to have actually been looking for. – Karl Knechtel Jun 29 '22 at 23:08
  • While this technically works, it is not what I would recommend. Most beginners who want to do this simply need to think of a better way to approach their problem. – tripleee Jul 25 '22 at 04:03
  • 1
    @tripleee: That's basically what I said in my [first comment](https://stackoverflow.com/questions/19326004/access-a-function-variable-outside-the-function-without-using-global/19327712?noredirect=1#comment28633948_19327712) — perhaps you missed it. – martineau Jul 25 '22 at 06:56
  • I had composed that comment originally in response to a different answer. I felt that it rearticulates what's in your comment in sufficiently different words that it might help someone who was unable to understand the implications in your comment, but I agree that there is some unfortunate overlap. – tripleee Jul 25 '22 at 10:11
12

The problem is you were calling print(x.bye) after you set x as a string. When you run x = hi() it runs hi() and sets the value of x to 5 (the value of bye; it does NOT set the value of x as a reference to the bye variable itself). EX: bye = 5; x = bye; bye = 4; print(x) prints 5, not 4.

Also, you don't have to run hi() twice, just run x = hi(), not hi(); x=hi() (the way you had it it was running hi(), not doing anything with the resulting value of 5, and then rerunning the same hi() and saving the value of 5 to the x variable.

So full code should be

def hi():
    bye = 5
    sigh = 10
    return bye 
x = hi()
print(x)

If you wanted to return multiple variables, one option would be to use a list, or dictionary, depending on what you need. For example:

def hi():
    return { 'bye': 5, 'sigh': 10 }
x = hi()
print x['bye']
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
John
  • 480
  • 3
  • 10
  • 1
    Thanks. This is helpful. Though I can't shake the feeling that there is a clean/documented trick to achieve it - to define multiple local variables and then call just one of them outside the function. – askance Oct 11 '13 at 21:42
3
 def hi():
     bye = 5
     return bye  

print hi()
Allan Mwesigwa
  • 1,210
  • 14
  • 13
2

You could do something along this lines:

def static_example():
   if not hasattr(static_example, "static_var"):
       static_example.static_var = 0
   static_example.static_var += 1
   return static_example.static_var

print static_example()
print static_example()
print static_example()
2

To be able to access a local function's variable, one might add the name of the function and a dot before the name of the local variable (and then, of course, use this construction for calling the variable both in the function's body and outside of it). This solution works in Python 3.7.4.

For example:

def func(): # define a function
    # here y is a local variable, which I want to access; func.y 
    # defines a method for my example function which will allow me to  
    # access function's local variable y
    func.y = 4 
    x = func.y + 8
    return x

func() # now I'm calling the function
a = func.y # I put its local variable into my new variable
print(a) # and print my new variable
  • 1
    This duplicates the accepted answer from 2013. – tripleee Jul 25 '22 at 04:01
  • 1
    @tripleee, if this duplicated the accepted answer, I wouldn't post it. Why do you think I posted it, out of love to type? I tried the accepted answer, it didn't work, I worked out my own solution. And as you may notice, my answer has 2 upvotes. Evidently, I wasn't the only one who couldn't make use of the accepted answer. Moreover, before Karl Knechtel edited my answer, it had included some comments where - if I remember it correctly - I specified that the accepted answer didn't work for me and that my solution was based on the other answers (that didn't work for me on their own). – Ivan Nepomnyashchikh Jul 25 '22 at 15:35
  • 1
    That is easy to check from the [revision history](https://stackoverflow.com/posts/51482838/revisions) (which is linked from the notice which tells you who made the latest edit). – tripleee Jul 25 '22 at 16:19
  • 1
    The code in your answer is fundamentally identical to the first half of the accepted answer, which still works fine. – tripleee Jul 27 '22 at 08:07
1

If you want to avoid global, one possible approach is to define a class. Each class instance has its own attributes; there is also a class attribute space where instances can share an attribute between them.

Object-oriented programming can be challenging to get into if you are new to Python, but this might actually be a good time to start playing with it.

class Thing:
    shared = "foo"

    def __init__(self):
        """
        This gets called when you create a new Thing()
        """
        self.bar = "baz"  # default value for new instances

    def get_bar(self):
        return self.bar

    def set_bar(self, value):
        self.bar = value

Now, let's create two instances.

first = Thing()
second = Thing()

The get_bar and set_bar methods are not strictly necessary in simple examples like this one. You can also do

second.bar = "ick"
print(second.bar)
# "ick"
print(first.bar)
# "baz"

(though for more complex scenarios, you probably want to require users to call the setter and getter methods; there are ways to force this - see e.g. What's the pythonic way to use getters and setters?)

If you change a class attribute via one instance, it will not be changed in the other instances, either.

second.shared = "poo"
print(first.shared)
# "foo"

But if you change it in the class itself, it will be changed in all the instances which have not separately overridden the shared value.

Thing.shared = "zoom"
print(first.shared)
# "zoom"
print(second.shared)
# "poo", still

To recap, you create a new Thing instance by calling Thing(); this will run the __init__ method before returning the new instance. Inside the class, the instance is the first argument to every (non-static, non-class) method, and conventionally called self (though you could get away with calling it shirley if you wanted to, as far as the Python interpreter is concerned).

There's a lot more to classes; the main selling point is probably that you can create subclasses which inherit from their parent class but can override some behaviors (common examples often involve real-world concepts like animals or vehicles, but a class can just be anything where you want to create a type and encapsulate its behavior, and perhaps override some methods in derived types).

tripleee
  • 175,061
  • 34
  • 275
  • 318