在之前的文章裏,我的多線程裝飾器雖然在我們的RF框架上用上了,但那是因爲我們裝飾的方法是靜態方法。如果裝飾的方法是寫在類中,由於第一個參數是self,這個參數在被裝飾時並不會傳入,只有調用時纔會在第一個位置傳入,導致解析參數時報錯。
定位發現問題是在
def multiThread(func): # 實現多線程
def infunc(*args):
print "診斷", args # 這裏加了這一行,然後發現後面的每個參數都是兩兩成對的元組,即每個線程的(*args,**kwargs),而第一個參數是孤零零的實例object,即self
global resset
resset = [] # 每次調用前,清除結果集
tli = [] # 線程對象列表
tid = 0 # 初始線程編號
定位後,解決這個問題不難,把代碼改變爲如下便可:
在迭代參數開始前先把第一個參數(self)拿走並保存
tli = [] # 線程對象列表
tid = 0 # 初始線程編號
# 在這裏新增一個判斷 #########
if type(args[0])!=tuple: # 說明原來的函數是類裏面的,第一個參數是self
sarg=args[0]
args=tuple(args[1:])
else:
sarg=None
###########################
for arg in args: # 處理傳入的參數列表
if len(arg) == 1:
接着在迭代結束後,執行前加上參數,也就是說原來的targ = tuple(list(targ) + [tid])改爲額外判斷,如果原來的類裏有self傳入,就把self拼在*args的最開頭,變成tuple([sarg] + list(targ) + [tid])
except:
raise ValueError, 'arguement format error'
targ = tuple(list(targ) + [tid]) if not sarg else tuple([sarg] + list(targ) + [tid]) # 在位置參數的最後一個位置,加上線程編號傳進去。getResult裏會把這個參數pop掉,並記錄到結果裏
tli.append(Thread(target=func, args=targ, kwargs=darg)) # 線程列表填充
tid = tid + 1 # 線程編號遞增
for th in tli:
th.setDaemon(True)
這裏是完整的腳本和測試代碼
# coding: utf-8
from threading import Thread
def getResult(func): # 重定向函數返回的結果
def infunc(*args, **kwargs):
args = list(args)
tid = args.pop() # 去掉最後一個代表線程編號的參數
args = tuple(args)
try:
resset.append((func(*args, **kwargs), tid)) # 結果是一個由二元元組組成的列表,每個線程一個元組,元組內第一個元素是線程運行的結果,第二個是線程編號
except Exception, e:
resset.append((e, tid)) # 如果線程內報錯,就返回錯誤對象
return infunc
def multiThread(func): # 實現多線程
def infunc(*args):
# print "診斷", args
global resset
resset = [] # 每次調用前,清除結果集
tli = [] # 線程對象列表
tid = 0 # 初始線程編號
if type(args[0])!=tuple: # 說明原來的函數是類裏面的,第一個參數是self
sarg=args[0]
args=tuple(args[1:])
else:
sarg=None
for arg in args: # 處理傳入的參數列表
if len(arg) == 1:
if type(arg[0]) == type({}):
targ = () # 設置位置參數
darg = arg[0] # 設置命名參數
else:
targ = arg[0]
darg = {}
elif len(arg) == 0 or arg == None:
targ = ()
darg = {}
else:
targ = arg[0] # (args,kwargs) [0]
darg = arg[1] # (args,kwargs) [1]
try:
assert type(targ) == type(()) and type(darg) == type({}) # 確定傳來的參數格式正確
except:
raise ValueError, 'arguement format error'
targ = tuple(list(targ) + [tid]) if not sarg else tuple([sarg] + list(targ) + [tid]) # 在位置參數的最後一個位置,加上線程編號傳進去。getResult裏會把這個參數pop掉,並記錄到結果裏
tli.append(Thread(target=func, args=targ, kwargs=darg)) # 線程列表填充
tid = tid + 1 # 線程編號遞增
for th in tli:
th.setDaemon(True)
th.start()
for th in tli:
th.join() # 大家都開始了才join阻塞
return {x[1]: x[0] for x in resset} # 結果字典,resset的結果是二元元組,0號元素是函數返回值,1號元素是線程編號tid
return infunc
def parseArg(*args, **kwargs): # 打包單個線程的參數
return (args, kwargs)
def multipleArg(gtuple, times): # 複製某個線程的參數n次,形成參數元組
return (gtuple,) * times
if __name__=="__main__":
############################################## 演示常規用法 #####################################
from selenium import webdriver
import time, re, random
@multiThread
@getResult
def baidusearch(statement):
driver = webdriver.Chrome()
driver.get(r'http://www.baidu.com')
driver.maximize_window()
driver.implicitly_wait(10)
kw = driver.find_element_by_id('kw')
kw.clear()
kw.send_keys(statement)
driver.find_element_by_id('su').click()
time.sleep(2)
resNumber = driver.find_element_by_xpath("//span[@class='nums_text']").text
resNumber = re.findall('[0-9,]+', resNumber)[0]
driver.close()
return resNumber
li1 = parseArg('mi')
li2 = parseArg('wo')
li3 = parseArg(u'故宮喵')
print(baidusearch(li1, li2, li3))
lis = multipleArg(parseArg('python'), 3) # 或者可以像下面這樣,連續生成多個同樣的關鍵字
print(baidusearch(*lis)) # 這種方式不要忘記解包
lit = tuple([parseArg('html' + str(x)) for x in range(1, 6)]) # 使用推導式,分別搜索html1 html2 ... html5
print(baidusearch(*lit))
################################# 可以使用路由的方式,讓不同的函數併發執行 #############################
def plus(x, y):
return x + y
def minus(x, y):
return abs(x - y)
@multiThread
@getResult
def union(func_name, *args, **kwargs): # 可以用這種方法來實現路由,讓不同的函數同時執行在一個線程裏
return eval(func_name)(*args, **kwargs)
li1 = parseArg('plus', 1, 2)
li2 = parseArg('minus', 3, y=5)
li3 = parseArg('plus', x=4, y=6)
print(union(li1, li2, li3))
#################################### 類裏面的函數也支持這樣處理了 ################################################
class testLibrary(object):
@multiThread
@getResult
def printer(self, tno):
'''
:param unicode tno: 線程編號
:return:
'''
startTime = time.time()
for i in range(50):
print tno, i
time.sleep(random.random())
return time.time() - startTime
@staticmethod
def parseArg(*args, **kwargs): # 打包單個線程的參數
return (args, kwargs)
@staticmethod
def multipleArg(gtuple, times): # 複製某個線程的參數n次,形成參數元組
return (gtuple,) * times
t=[parseArg(x,) for x in range(4)]
lib=testLibrary()
print lib.printer(*t)