1

I am interested in Twisted python and was doing my weekly "keep-up-to-date-and-practice" reading at http://jdb.github.io/concurrent/preemptive.html.

Summary: what is the difference between using incr() and incr as arguments? when I declare the function as incr() i get "correct" results even with large numbers. However, when I declare it as incr I get wrong results. A fast search on google just shows me how to declare functions within classes.

Thread(target=incr, args=(i,))

and

Thread(target=incr(), args=(i,))

where incr is:

counter = 0

def incr(num):
    global counter
    count = 0
    for i in range(1000):
        counter += 1
        print "\nWorker: {}, Counter: {}, Self-count: {} .".format(num, counter, count)

    print counter

3 Answers 3

2

Let me show you.

Let's define some test function. Which will print something when it's fired

>>> def test():
...     print "fired!"
... 

Now let's try to define the threads.

>>> Thread(target=test)
<Thread(Thread-1, initial)>
>>> Thread(target=test())
fired!
<Thread(Thread-2, initial)>

Can you see it? When you write test() function is fired before you start the thread. But what will happen if we will try to run this threads?

>>> thread1.start()
fired!

Good

>>> thread2.start()
>>> 

Nothing. That's because when you write Thread(target=test) you pass an instance of test function as argument. When you write Thread(target=test()) you pass result of test() function execution (None) :)

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

Comments

1

When you type function() those parentheses tell Python to run that function in that spot. So

x = function()

means that x will be set as the value that function returns when it runs. But if you leave out the brackets, you're not referring to the returned value, but the function itself. So

x = function

Means that x is now the function, and could be called with x().

In your case, it's the difference between passing what incr returns and the function itself.

Comments

0

In your example, target=incr() would die trying to run incr in the main thread without passing an argument (and even if it succeeded, it would do nothing when it tried to launch a thread with None as the target; None is what incr implicitly returns). Running with target=incr would perform the incrementing in a thread as expected.

The case for using target=incr() would be to use a closure to avoid polluting global scope. For example, in Python 3, you could do:

def incr():
    counter = 0

    def incr_inner(num):
        nonlocal counter
        count = 0
        for i in range(1000):
            counter += 1
            print("\nWorker: {}, Counter: {}, Self-count: {} .".format(num, counter, count))

        print(counter)
    return incr_inner

which would create a per-thread counter not shared in the global scope; by setting target=incr(), it would call the outer incr and return the enclosed function incr_inner as the real target, with its own unique counter variable.

The nesting as a closure is mostly about implementation hiding and brevity (and it doesn't always work as you'd hope; in Python 2, without the nonlocal keyword, it can't be made to run as written without some hackery). But you can make callable objects from a class in any version of Python, and get roughly the same "stateful thread" behavior. For example:

class incr(object):
    def __init__(self):
        self.counter = 0

    def __call__(self, num):
        count = 0
        for i in range(1000):
            self.counter += 1
            print("\nWorker: {}, Counter: {}, Self-count: {} .".format(num, self.counter, count))

        print(self.counter)

And just like the closure case, this class could be used as an argument that maintained independent counters for each thread when passed as target=incr().

2 Comments

thank you for your swift answer. could you emphasize more on the lower level details about the difference between how incr and incr() is interpreted by python and if they both have the same pointer address?
@laycat: incr is a function (a callable, that Python can invoke to perform the thread work; functions are first class objects in Python, so if you refer to them without parentheses, you get a reference to the function). incr() calls the function, and evaluates to whatever the function returns; that is, it happens before the Thread is even constructed, so if the work is done during that call, you didn't actually use the Thread. The only way it would be legal to initialize a Thread to do real work would be if calling the function produced another callable, as in my closure example.

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.