RecursionError al llamar a la función pickeled [duplicar]

Nov 12 2020

Estoy intentando ejecutar el siguiente código:

import pickle

def foo():
    print("i am foo")
    
pickle_foo = pickle.dumps(foo)

def foo():
    print("i am the new foo")
    fkt = pickle.loads(pickle_foo)
    return fkt()

foo()

El comportamiento esperado sería:

  • la nueva función definida "foo" se llama
  • en la nueva función, la función anterior no se modifica y luego se llama
    salida:
i am the new foo
i am foo

Lo que realmente sucede es:

  • se llama a la nueva función foo, y luego se llama recursivamente a sí misma hasta que se lanza un error de recursividad:
RecursionError: maximum recursion depth exceeded while calling a Python object

El error no ocurre cuando las dos funciones se nombran de manera diferente, pero eso sería muy poco práctico para mi proyecto. ¿Alguien podría explicar por qué ocurre este comportamiento y cómo evitarlo (sin cambiar los nombres de las funciones)?

Respuestas

3 Stephen Nov 12 2020 at 21:08

El picklemódulo selecciona funciones en función de su referencia de nombre completo. Esto significa que si tu función se redefine en algún lugar del código, y luego eliminas una referencia en escabeche a ella, llamarla resultará en una llamada a la nueva definición.

De los documentos de Python en pickle:

Tenga en cuenta que las funciones (integradas y definidas por el usuario) se seleccionan por referencia de nombre "totalmente calificado", no por valor. 2 Esto significa que sólo se selecciona el nombre de la función, junto con el nombre del módulo en el que está definida la función. No se selecciona el código de la función ni ninguno de sus atributos de función. Por lo tanto, el módulo de definición debe ser importable en el entorno de eliminación de fallas y el módulo debe contener el objeto nombrado; de lo contrario, se generará una excepción.

Sin embargo, lo que puede hacer es usarlo inspect.getsource()para recuperar el código fuente de su función y seleccionarlo. Esto requiere que su código esté disponible como fuente en algún lugar del sistema de archivos, por lo que el código C compilado importado u otras fuentes externas (entrada del intérprete, módulos cargados dinámicamente) no funcionarán.

Cuando lo desenrolla, puede usarlo execpara convertirlo en una función y ejecutarlo.

Nota: esto se redefinirá foocada vez, por lo que foono se puede garantizar que las llamadas a tengan el mismo efecto.

Nota 2: execno es seguro y, por lo general, no es adecuado para el código que interactuará con fuentes externas. Asegúrese de proteger las llamadas a execde posibles ataques externos que intenten ejecutar código arbitrario.