[英]python multiprocessing : setting class attribute value
我有一個名為Experiment的類,另一個名為Case。 一個實驗由許多個案組成。 見下面的類定義,
from multiprocessing import Process
class Experiment (object):
def __init__(self, name):
self.name = name
self.cases = []
self.cases.append(Case('a'))
self.cases.append(Case('b'))
self.cases.append(Case('c'))
def sr_execute(self):
for c in self.cases:
c.setVars(6)
class Case(object):
def __init__(self, name):
self.name = name
def setVars(self, var):
self.var = var
在我的實驗課中,我有一個名為sr_execute的函數。 此功能顯示所需的行為。 我有興趣解析所有案例並為每個案例設置一個屬性。 當我運行以下代碼時,
if __name__ == '__main__':
#multiprocessing.freeze_support()
e = Experiment('exp')
e.sr_execute()
for c in e.cases: print c.name, c.var
我明白了
a 6
b 6
c 6
這是期望的行為。
但是,我想使用多處理並行執行此操作。 為此,我向實驗類添加了一個mp_execute()函數,
def mp_execute(self):
processes = []
for c in self.cases:
processes.append(Process(target= c.setVars, args = (6,)))
[p.start() for p in processes]
[p.join() for p in processes]
但是,這不起作用。 當我執行以下操作時
if __name__ == '__main__':
#multiprocessing.freeze_support()
e = Experiment('exp')
e.mp_execute()
for c in e.cases: print c.name, c.var
我收到一個錯誤,
AttributeError: 'Case' object has no attribute 'var'
顯然,我無法使用多處理設置類屬性。
發生了什么事情的線索,
你打電話的時候:
def mp_execute(self):
processes = []
for c in self.cases:
processes.append(Process(target= c.setVars, args = (6,)))
[p.start() for p in processes]
[p.join() for p in processes]
當您創建Process
,它將使用您的對象的副本 ,並且對此類對象的修改不會傳遞給主程序,因為不同的進程具有不同的地址空間。 如果您使用Thread
s,它將起作用,因為在這種情況下不會創建副本。
另請注意,您的代碼可能在Windows中失敗,因為您將方法作為target
傳遞,而Windows要求target
可被選擇(並且實例方法不可選)。 target
應該是在模塊頂層定義的函數,以便在所有Oses上工作。
如果您想與主進程通信,您可以進行以下更改:
無論如何,您必須通過設置“通道”(如Queue
)或設置共享狀態來“明確”處理通信。
樣式注意:以這種方式不使用列表內涵:
[p.join() for p in processes]
這完全是錯的 。 你只是浪費空間創建列表None
秒。 與正確的方式相比,它也可能更慢:
for p in processes:
p.join()
因為它必須將元素附加到列表中。
有人說,列表內涵是比稍快for
循環,但是:
當且僅當您考慮這種循環時,它們會更快:
a = [] for element in something: a.append(element)
如果循環,像在這種情況下, 不會創建一個list
,然后for
循環會更快。
順便說一句:有些人以相同的方式使用map
來執行副作用。 這又是錯誤的,因為你不會因為和以前一樣的原因獲得很多速度而且它在python3中完全失敗,其中map
返回一個迭代器,因此它根本不會執行函數,從而使代碼不那么便攜。
@ Bakuriu的回答提供了良好的造型和效率建議。 確實,每個進程都獲得主進程堆棧的副本 ,因此除非您使用某種形式的IPC(例如Queue,Pipe,Manager),否則分叉進程所做的更改不會反映在主進程的地址空間中。
但具體的AttributeError: 'Case' object has no attribute 'var'
,你得到了一個額外的理由,即您的案例對象還沒有錯誤var
在您啟動過程中的時間屬性。 相反, var
屬性是在setVars()
方法中創建的。
你的分叉進程在調用setVars()
時確實創建了變量 (實際上甚至將它設置為6),但是,這種變化只在Case對象的副本中,即沒有反映在主進程的內存空間中(變量仍然不存在)。
要了解我的意思,請將Case類更改為:
class Case(object):
def __init__(self, name):
self.name = name
self.var = 7 # Create var in the constructor.
def setVars(self, var):
self.var = var
通過在構造函數中添加var
成員變量,您的主進程將可以訪問它。 當然,分叉進程中的更改仍然不會反映在主進程中,但至少您不會收到錯誤:
a 7
b 7
c 7
希望這能夠揭示正在發生的事情。 =)
解:
最少侵入(對原始代碼)要做的是使用共享內存中的ctypes對象:
from multiprocessing import Value
class Case(object):
def __init__(self, name):
self.name = name
self.var = Value('i', 7) # Use ctypes "int" from shared memory.
def setVars(self, var):
self.var.value = var # Set the variable's "value" attribute.
並更改您的main()以打印c.var.value:
for c in e.cases: print c.name, c.var.value # Print the "value" attribute.
現在您有了所需的輸出:
a 6
b 6
c 6
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.