Python threading.Thread 錯誤筆記

How to fix the error 'TypeError: subProgram() argument after * must be an iterable, not Queue' in python threading programming.

October 15, 2017 - 3 minute read -
python

最近在試著用 Python 寫多線程的程式遇到一點小狀況,範例程式如下:

import time
import threading
from multiprocessing import Queue

def subProgram(queue):
    for i in range(10):
        queue.put(i)

def main():

    queue = Queue()

    thread = threading.Thread(target=subProgram, args=(queue))
    thread.start()

    time.sleep(2)
    while not queue.empty():
        print queue.get()

main()

看似沒問題的程式碼,執行後卻發生錯誤:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
TypeError: subProgram() argument after * must be an iterable, not Queue

在 stackoverflow 找到了類似的問題跟解法:

You need to add , after s sending just s to args=() is trying to unpack a number of arguments instead of sending just that single arguement.

但是因為對 Python 太不熟了,惡補了一下基本語法跟翻了一下手冊,確定 args 是 tuple 的型態

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}) This constructor should always be called with keyword arguments. Arguments are:

  • group should be None; reserved for future extension when a ThreadGroup class is implemented.
  • target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.
  • name is the thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number.
  • args is the argument tuple for the target invocation. Defaults to ().
  • kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}.

If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.init()) before doing anything else to the thread.

tuple 如果只有單一元素 (element) 的話必須加個,逗號才行,不然就會被解讀成單一物件。

參考以下範例會比較了解:

>>> a = (111,222)
>>> a
(111, 222)
>>> 
>>> a = (111, 222)
>>> a
(111, 222)
>>> b = (111)
>>> b
111
>>> c = (111,)
>>> c
(111,)

可以看到 b = (111) 被解讀成 111 了,這並不是預期的狀況,改成 c = (111, ) 就會正常解析成 tuple 型態了。

同理,只要在

thread = threading.Thread(target=subProgram, args=(queue))

args=(queue) 補上 , 符號就解決這項錯誤:

thread = threading.Thread(target=subProgram, args=(queue,))

最後修正的程式如下:

import time
import threading
from multiprocessing import Queue

def subProgram(queue):
    for i in range(10):
        queue.put(i)

def main():

    queue = Queue()

    thread = threading.Thread(target=subProgram, args=(queue,))
    thread.start()

    time.sleep(2)
    while not queue.empty():
        print queue.get()

main()

Reference: