El uso de multiprocesamiento dentro del decorador genera un error: no se puede encoger la función … no se encuentra como

Encontré un problema que no puedo resolver y está asociado con el multiprocesamiento y su uso dentro del decorador.

Cuando llamo al método run_in_parallels usando multiprocesamiento, run_in_parallels el error:

Can't pickle : it's not found as __main__.run_testcase

La llamada tiene lugar dentro del decorador, luego se sigue el problema mencionado anteriormente. En el momento de llamar al mismo método, run_in_parallels sin un decorador funcionan correctamente.

¿Cuál es la razón de este problema?


archivo: w_PythonHelper.py

desc: la función ‘run_in_parallel’ se usa para ejecutar múltiples procesos simultáneamente. El primer método, que terminará la operación, detiene a los demás.

 from multiprocessing import Process,Event class ExtProcess(Process): def __init__(self, event,*args,**kwargs): self.event=event Process.__init__(self,*args,**kwargs) def run(self): Process.run(self) self.event.set() class PythonHelper(object): @staticmethod def run_in_parallel(*functions): event=Event() processes=dict() for function in functions: fname=function[0] try:fargs=function[1] except:fargs=list() try:fproc=function[2] except:fproc=1 for i in range(fproc): process=ExtProcess(event,target=fname,args=fargs) process.start() processes[process.pid]=process event.wait() for process in processes.values(): process.terminate() for process in processes.values(): process.join() 

archivo: w_Recorder.py

desc: la función ‘captura’ se utiliza para capturar una captura de pantalla

 from PIL import ImageGrab import time class Recorder(object): def capture(self): ImageGrab.grab().save("{f}.{e}".format(f=time.time(),e="png")) 

archivo: w_Decorators.py

desc: Ejecutar en paralelo una función determinada junto con un método ‘captura’ de la clase ‘Grabadora’

 from w_Recorder import Recorder from w_PythonHelper import PythonHelper def check(function): def wrapper(*args): try: recorder=Recorder() PythonHelper.run_in_parallel([function,args],[recorder.capture]) print("success") except Exception as e: print("failure: {}".format(e)) return function return wrapper 

archivo: w_Logger.py

desc: progtwig principal (genera error)

 from w_Decorators import check import time class Logger(object): @check def run_testcase(self): # example function (runtime: 20s) for i in range(20): print("number: {}".format(i)) time.sleep(1) def run_logger(self): self.run_testcase() if __name__=="__main__": logger=Logger() logger.run_logger() 

archivo: w_Logger.py

desc: progtwig principal (funciona correctamente)

 from w_PythonHelper import PythonHelper from w_Recorder import Recorder import time class Logger(object): def run_testcase(self): # example function (runtime: 20s) for i in range(20): print("number: {}".format(i)) time.sleep(1) def run_logger(self): recorder=Recorder() PythonHelper.run_in_parallel([self.run_testcase],[recorder.capture]) if __name__=="__main__": logger=Logger() logger.run_logger() 

¿Cuál es la diferencia de que estos mismos métodos presentados en los dos casos funcionan de manera diferente?


EDITAR: ¿Alguien tiene una idea para resolver este problema (es este error de Python)? Si no es así, ¿tal vez alguien sabe una buena forma de capturar capturas de pantalla cuando la aplicación se está ejecutando?

En realidad, encontré una pregunta similar: multiprocessing.Process subclass funciona en Linux pero no en Windows

La respuesta es: To fix this, you can remove the process member. , pero ¿cómo puedo hacer esto para mi ejemplo.

Al depurar, el error se produce al llamar a process.start() en run_in_parallel(*functions)


EDIT2: al igual que ivan_pozdeev escribió: puedo usar el envoltorio como una función, pero no puedo usarlo como decorador. Tengo muchas funciones decoradas por este decorador y la forma más sencilla es utilizar el multiprocesamiento dentro del decorador. Pero desafortunadamente no puedo resolver este problema. Tal vez alguien ya haya resuelto un problema similar. Estaría agradecido por cualquier pista.

La función ‘run_in_parallel’ funciona como yo quiero. Dos o más funciones se ejecutan en paralelo y la primera función, que se completa, fuerza la terminación de la segunda función. Cuando llamo wrapper (function, * args) , las funciones funcionan bien, cuando coloco este mecanismo dentro del decorador, obtengo la función “no puedo encoger” … no se encuentra como “ error . Los detalles se pueden encontrar arriba

Mi Traceback:

 Traceback (most recent call last): File "C:\Interpreters\Python32\lib\pickle.py", line 679, in save_global klass = getattr(mod, name) AttributeError: 'module' object has no attribute 'run_testcase' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\EskyTests\w_Logger.py", line 19, in  logger.run_logger() File "C:\EskyTests\w_Logger.py", line 14, in run_logger self.run_testcase() File "C:\EskyTests\w_Decorators.py", line 14, in wrapper PythonHelper.run_in_parallel([function,args],[recorder.capture]) File "C:\EskyTests\w_PythonHelper.py", line 25, in run_in_parallel process.start() File "C:\Interpreters\Python32\lib\multiprocessing\process.py", line 130, in start self._popen = Popen(self) File "C:\Interpreters\Python32\lib\multiprocessing\forking.py", line 267, in __init__ dump(process_obj, to_child, HIGHEST_PROTOCOL) File "C:\Interpreters\Python32\lib\multiprocessing\forking.py", line 190, in dump ForkingPickler(file, protocol).dump(obj) File "C:\Interpreters\Python32\lib\pickle.py", line 237, in dump self.save(obj) File "C:\Interpreters\Python32\lib\pickle.py", line 344, in save self.save_reduce(obj=obj, *rv) File "C:\Interpreters\Python32\lib\pickle.py", line 432, in save_reduce save(state) File "C:\Interpreters\Python32\lib\pickle.py", line 299, in save f(self, obj) # Call unbound method with explicit self File "C:\Interpreters\Python32\lib\pickle.py", line 623, in save_dict self._batch_setitems(obj.items()) File "C:\Interpreters\Python32\lib\pickle.py", line 656, in _batch_setitems save(v) File "C:\Interpreters\Python32\lib\pickle.py", line 299, in save f(self, obj) # Call unbound method with explicit self File "C:\Interpreters\Python32\lib\pickle.py", line 683, in save_global (obj, module, name)) _pickle.PicklingError: Can't pickle : it's not found as __main__.run_testcase 

La función que está pasando al Process.__init__() no se puede seleccionar en Windows. Leer 16.6 multiprocesamiento – Pautas de progtwigción – Windows .

Con respecto a su error con una función de nivel superior: sospecho que la forma en que lo definió, se genera de manera diferente cada vez y, por lo tanto, realmente no es el mismo objeto en niños. Sugiero pasar una función simple de nivel superior que llamaría run_testcase usando reflexion si realmente necesita este nivel de sofisticación. Actualización: esto no ayudó

Actualizar:

Hice este trabajo sin decorar run_testcase , run_in_parallel y capture . @check decorator fue reemplazado por def wrapper(function,*args) con la misma funcionalidad:

 import traceback def wrapper(function,*args): try: recorder=Recorder() PythonHelper().run_in_parallel([function,args],[recorder.capture]) print("success") except Exception,e: print("failure: "+traceback.format_exc(10)) 

principal:

 from w_Decorators import wrapper if __name__=="__main__": logger=Logger() wrapper(logger.run_testcase) 

Justo como pensaba, los objetos decorados no son escamoteables.

Difícil, pero creo que lo que está sucediendo es que la check almacena una referencia al método independiente cuando se define la clase. El ejemplo de trabajo utiliza una referencia al método enlazado self.run_testcase cuando llama a run_logger .

Creo que lo mejor es intentar hacer que run_testcase una función de nivel superior, en lugar de un método de una clase.

Además, su función de capture probablemente no hará lo que espera: la hora actual se almacenará cuando se defina la función, y cada captura de pantalla se guardará sobre la anterior. Probablemente quieras llamar a time.time() dentro de la función.