4

Both codes seem to have similar performance. How does the scope work in this case? Is any of them better than the other? Is there a better way to achieve the same behavior?

code 1:

class ex:
  b = 6
  def foo(self, a):
    def fooHandler(a):
      while True:
        print a
        time.sleep(1)
    threading.Thread(target=fooHandler, args=(a,)).start()
x = ex()
x.foo(10)
x.foo(100)
x.foo(1000)

code 2:

class ex:
  b = 6
  def foo(self, a):
    def fooHandler():
      while True:
        print a
        time.sleep(1)
    threading.Thread(target=fooHandler).start()
x = ex()
x.foo(10)
x.foo(100)
x.foo(1000)
user3666197
  • 1
  • 6
  • 50
  • 92
  • the indentation was messed up when I copied from my editor, it should be working now –  Aug 02 '16 at 10:07
  • You may want to read this: http://stackoverflow.com/questions/4020419/why-arent-python-nested-functions-called-closures – dmitri Aug 03 '16 at 01:39
  • @dmitri the link was helpful :) thank you –  Aug 09 '16 at 17:21

2 Answers2

1

Well, there is a difference in the generated code (at least when using CPython 2.7.12):

def runThread(a):
    def threadFunc():
        while True:
            print a
            time.sleep(1)

    t = threading.Thread(target=threadFunc)
    t.start()

Will issue a LOAD_GLOBAL opcode for a inside threadFunc() (output is from inspect.dis.dis()):

8           9 LOAD_GLOBAL              1 (a)

while

def runThread(a):
    def threadFunc(a):
        while True:
            time.sleep(1)

    t = threading.Thread(target=threadFunc, args=(a, ))
    t.start()

will issue a LOAD_FAST opcode:

8           9 LOAD_FAST                0 (a)

The LOAD_FAST happens, because the compiler knows that a is parameter and thus the lookup only needs to happen wrt. to the current namespace. LOAD_FAST (hence the name) is potentially faster than LOAD_GLOBAL, but if you need think about the differences in terms of performance, you probably shouldn't be using Python in the first place.

And yeah, everything screams "implementation detail" to me, too.

Scope-importing a from an outer scope gives you added flexibility, since you can still modify a even after the thread is already running. When passing a as parameter to the thread function, that possibility is more or less gone. In any case, I would consider the former an antipattern unless its a is the thread termination flag.

dhke
  • 15,008
  • 2
  • 39
  • 56
0

This has nothing to do with threading, it's just a matter of using a value from nested scope vs. passing it explicitly as an argument and using it from local scope.

It doesn't particularly matter which approach you use in this case; you're using the same value in either case. Looking it up in nested scope is more expensive, but only trivially so (it's essentially equivalent to a single dict lookup, where local scope lookups are closer to array access performance).

If you needed to change the binding of a during the function, you could not do so with implicit nested scope access (you'd have to use a different name), because you can't read and write from a nested scope variable in Python 2 (which lacks the nonlocal keyword). Using a different name (initially set to a) would be similar to accepting it as an argument in any event, so again, no major distinction.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271