0

I have the following simplified version of my code:

import time


def inner(condition, callback):
    while condition:
        print('test')
        time.sleep(1)
        callback()


def outer():
    condition = True

    def callback():
        nonlocal condition
        condition = False

    inner(condition, callback)


outer()

Currently, the above code continues to print, even though i change the condition variable to false in the callback function. I want the condition to change to False, and the while loop to exit.

How come the variable change is not detected in the inner func?

Mike Hawkins
  • 2,144
  • 5
  • 24
  • 42
  • The current value of `condition` is copied into the `condition` variable in `inner`. It's not the same variable as the one in `outer`. – khelwood Oct 07 '20 at 09:25
  • I thought python passed arguments by reference? – Mike Hawkins Oct 07 '20 at 09:25
  • No, Python does not pass arguments by reference. – khelwood Oct 07 '20 at 09:26
  • 2
    [How do I pass a variable by reference?](https://stackoverflow.com/q/986006/3890632) – khelwood Oct 07 '20 at 09:27
  • Obviously. Thanks for the reference. – Mike Hawkins Oct 07 '20 at 09:27
  • 1
    The problem is the `condition` variable value is of type boolean which are immutable. You _can_ effectively pass references to objects which are mutable like a `list`s and `dict`s. For example use `condition = [True]` and you can change the value of its first element via `condition[0] = False` or test its current value with `while condition[0]:` in the `inner()` function. – martineau Oct 07 '20 at 10:12

1 Answers1

1

That's because the condition in inner() is a local variable, callback() changes the condition defined in the 1st line of outer(), but it cannot change the local variable of inner(). So, the condition in inner will always be True. This can be verified by the following code

def inner(condition, callback):
    # while condition:
    #     print('test')
    #     time.sleep(1)
    #     callback()
    print('condition in inner before callback(): {}'.format(condition))
    callback()
    print('condition in inner after callback(): {}'.format(condition))




def outer():
    condition = True

    def callback():
        nonlocal condition
        condition = False

    inner(condition, callback)
    print('condition in outer after callback() called in inner: {}'.format(condition))


outer()

One work around is changing the condition to a list: [True]. List is mutable object in python, which means changing it's element will not change it's adress in memory, the condition in inner() keeps updating with the condition in outer() :

def inner(condition, callback):
    while condition[0]:
        print('test')
        time.sleep(1)
        callback()


def outer():
    condition = [True]

    def callback():
        nonlocal condition
        condition[0] = False

    inner(condition, callback)

outer()