2

So it seems I'm misunderstanding some basic stuff about Python. Passing an instance variable into a function should only pass a reference to the object itself, so in case the object is immutable doing something like self.var = "something"; foo(self.var) shouldn't change self.var if foo assigned a new value to the variable - so long everything's fine and as expected.

But now consider this:

import threading

class Test():
    def __init__(self):
        self.lock = threading.Lock()
        self.arg = "arg0"

    def foo(self, i):
        with self.lock:
            threading.Thread(target=lambda: bar(self.arg)).start()
            self.arg = "arg" + str(i)

def bar(arg):
    import time
    time.sleep(1)
    print("Bar: " + arg)

if __name__ == '__main__':
    t = Test()
    for i in range(1, 6):
        t.foo(i)

I create a thread object with a reference to the current string and afterwards update it - which the thread shouldn't see. Thanks to the lock the next thread also should only start after the update - so while I can't make any assumptions about the sequence in which arg0-5 will be printed I'd assume every arg should be printed exactly once. But I get the following output (Win7 x64, python 3.1 x64)

Bar: arg0
Bar: arg2
Bar: arg2
Bar: arg5
Bar: arg3

Edit: Okay after typing this up, I had the glorious idea that probably the lambda expression isn't executed when creating the thread but later on, which would explain the behavior, so the simple workaround would be to just create a local variable and use that. Well - now that was fast help by SO ;)

3
  • Congrats! You solved the riddle. Another thing: What do you think class Test(): does, as opposed to class Test: or class Test(object):? Commented Aug 13, 2011 at 2:07
  • @pillmuncher: I'm using Python3 so I luckily don't have to worry about old vs. new-style classes. Though I didn't know that class Test: was legal syntax - a bit nicer that way, great (for python2 that would still default to old class syntax though wouldn't it?) Commented Aug 13, 2011 at 2:16
  • @Voo if you solved your own problem, post it as an answer, then you can accept it after two days. Commented Aug 17, 2011 at 0:41

1 Answer 1

2

Since I noticed I still hadn't answered that one, here we go:

The lambda creates a closure to self but not to self.arg itself, which means when we later execute bar we access the newest value and not the one at lambda creation time.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.