1

I have been trying to return a variable in a function in a variable and use it outside of it:

test = 0

def testing():
    test = 1
    return test

testing()
print(test)

But when I run it, the result is 0. How could I fix this problem?

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
alexswear
  • 625
  • 2
  • 9
  • 10
  • Change the name of the global variable `test` to `g_test` and you'll understand. They are two totally different variables, and by using the same name, you're just confusing yourself. – Jonathon Reinhart Aug 04 '13 at 03:46
  • There are 7 votes as of this writing on an answer that looks right. But I don't think anyone actually explained this. The real glitch in your code (the only reason it does not work), is that when you do the function call, you do not capture the return value. Then you print an original unchanged global variable. Comments on scope are correct. "test" inside your function is local scope and is a copy that does not change the global "test" variable that is outside the function. – TMWP Mar 17 '17 at 13:17
  • If you are new to Python though, you should google "mutating versus nonmutating" and read posts on Stack Overflow about that too for when you work with lists and get a different behavior from seemingly similar code. If you pass "test" in as an argument, you still have to return it and capture it for it to work. But this behavior is different for mutating variables like lists. Hope you find this helpful in addition to the code fixes already provided by others to your question. – TMWP Mar 17 '17 at 13:17
  • oops - this is a 3 year old post. Guess I just wrote that up for completeness for anyone who stumbles onto this thread while looking to solve a problem. – TMWP Mar 17 '17 at 13:18

5 Answers5

11

You are messing up a bit the scopes and/or assignment. Try this:

def testing():
    test = 1
    return test

test = testing()
print(test)

Explanation: The test inside testing is different to the test inside the module. You have to assign it on module-level to get the expected result.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87
  • Thank you for this. I had a much more complex piece of code with recursion in it that was apparently failing for the same reason. It was just harder to see inside all of the code. This simple question and answer I think may have just put me onto the solution. :-) – TMWP Mar 17 '17 at 12:58
2

Because you declare test in the function, it is not a global variable, thus, you can not access the variable test you created in the function outside of it as they are different scopes

If you want to return test to a variable, you have to do

result = testing()
print(result)

Or, you can also add a global statement:

test = 0

def testing():
    global test
    test = 1
    return test

testing()
print(test)

By the way, when doing a conditional statement, you don't need the brackets around the 1==1 :).

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
TerryA
  • 58,805
  • 11
  • 114
  • 143
2

TLDR: A return value must be assigned to something at the call site.

test = testing()

Functions in Python have their own scope. It is created on entering (calling) the function, and destroyed when leaving it. Assignment to a name inside a scope makes that name local to this scope - causing it to be destroyed along with the scope.

# start outer scope
test = 0  # create name outer:test

def testing():
    # start inner scope
    test = 1  # create name outer.testing:test
    return test
    # end inner scope
    # destroy name outer.testing:test

testing()  # new temporary inner scope outer.testing
print(test)  # use name outer:test
# end outer scope

Notably, names in an inner scope may "shadow" names from an outer scope. While the name test exists in both testing and the outer scope, it does not refer to the same thing. This has two important implications:

  1. The assignment to the inner test does not affect the outer test.
  2. At the end of testing, the inner test is destroyed and only the outer test remains.

This is why calling testing() does not have the desired effect: it never modifies the outer test passed to print.


The return statement defines the value returned by calling a function. It does not return the name, only the value pointed to.

def testing():
    test = 1  # test refers to the value 1
    return test  # return test => value 1

The value returned by a function is like any other value - be it from a literal, lookup, or other. Most importantly, the value does not persist unless you assign it to a name or use it directly.

testing()  # call test, discard its value
test = testing()  # call test, store its value as `test`
print(testing())  # call test, use its value in `print`

So in order to return something from a function for later use, you must store the result to a name. You can then use that name in a later statement. A minimal example for your case looks like this:

# we already can define testing here
# it is overwritten later on, then

def testing():
    # all names we use inside of testing are gone at the end
    # if we just want a value, we can skip temporary names
    return 1

# store the return value of testing() for later use
test = testing()
print(test)

Addendum: It is possible for a function to modify its containing scope. However, names must then be explicitly declared as being from a foreign scope.

The nonlocal and global keywords allow to modify names from outer scopes. A nonlocal is the name in the closest matching function scope. A global is the name at the module scope, regardless of any functions in-between.

test = 0

def increment():
    global test  # declare test as belonging to a specific scope
    test += 1
    # no need to return something
    # we already modified the outer `test`

print(test)  # 0
increment()
print(test)  # 1

Note that modifying outer names is often the sign of an anti-pattern, moreso for globals than nonlocals. Beyond small scripts, it gets difficult to trace what is accessing and modifying globals. Often, it is more appropriate to use classes or generators to hold state.

A function can always read names from its containing scope, provided it never writes to the same name. Such closures are very easy to create, and the lack of modification makes them easier to trace. Note that modifying a name anywhere in a function makes it local, unless declared global or nonlocal:

test = 0

def increment():
    global test
    test += 1

def show_test():
    # we never modify `test`, so it is fetched from the outside
    print(test)

def show_and_increment1():  # this function is broken!
    print(test)  # `test` is *not* the outer one, since we modify it in the function
    test += 1  # modifying `test` makes it local for the *entire* function

def show_and_increment2():  # this function works!
    global test  # force `test` to be global
    print(test)
    test += 1

show_test()  # 0
increment()
show_test()  # 1
show_and_increment2()  # 1
show_and_increment2()  # 2
show_and_increment2()  # 3
show_test()  # 4
show_and_increment1()  # UnboundLocalError: local variable 'test' referenced before assignment
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
1

Inside the function testing(), you're creating a new variable test, not referring to the one that already exists. If you want to do that, you should use a global statement in the top, as in:

def testing():
    global test
    ...etc...
Eli Rose
  • 6,788
  • 8
  • 35
  • 55
  • This is also useful, but the other answer highlights a possible bug in the code (not capturing the return value) so it has wider implications as a solution and is more likely to help the poster find what went wrong in the real code that may have led to this example. Being new to Python, I had not realized you could do what is in your answer though and this may help me with other use cases, so thanks for posting. – TMWP Mar 17 '17 at 13:04
1

Your test variable inside the function does not have a global scope. So, if you want to store the return value in a variable and output it after that, you can do something like this:

result = testing()
print(result)
Amal Murali
  • 75,622
  • 18
  • 128
  • 150