RecursionError ao chamar a função selecionada [duplicar]

Nov 12 2020

Estou tentando executar o seguinte 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()

O comportamento esperado seria:

  • a nova função definida "foo" é chamada
  • na nova função, a função antiga é removida e então chamada de
    saída:
i am the new foo
i am foo

O que realmente acontece é:

  • a nova função foo é chamada e, em seguida, chama a si mesma recursivamente até que um erro de recursão seja lançado:
RecursionError: maximum recursion depth exceeded while calling a Python object

O erro não ocorre quando as duas funções são nomeadas de forma diferente, mas isso seria muito pouco prático para o meu projeto. Alguém poderia explicar por que esse comportamento ocorre e como evitá-lo (sem alterar os nomes das funções)?

Respostas

3 Stephen Nov 12 2020 at 21:08

O picklemódulo seleciona funções com base em sua referência de nome totalmente qualificado. Isso significa que, se sua função for redefinida em algum lugar no código e, em seguida, você retirar uma referência conservada a ela, chamá-la resultará em uma chamada para a nova definição.

Dos documentos do Python em pickle:

Observe que as funções (integradas e definidas pelo usuário) são conservadas por referência de nome "totalmente qualificado", não por valor. 2 Isso significa que apenas o nome da função é conservado, junto com o nome do módulo no qual a função está definida. Nem o código da função, nem qualquer um de seus atributos de função são conservados. Portanto, o módulo de definição deve ser importável no ambiente de remoção de piche, e o módulo deve conter o objeto nomeado, caso contrário, uma exceção será levantada.

O que você pode fazer, no entanto, é usar inspect.getsource()para recuperar o código-fonte de sua função e selecioná-lo. Isso requer que seu código esteja disponível como fonte em algum lugar no sistema de arquivos, portanto, o código C compilado importado ou outras fontes externas (entrada do interpretador, módulos carregados dinamicamente) não funcionarão.

Ao retirá-lo, você pode usar execpara convertê-lo em uma função e executá-lo.

Observação: isso será redefinido footodas as vezes, portanto, as chamadas para foonão podem ter a garantia de ter o mesmo efeito.

Nota 2: execnão é seguro e geralmente não é adequado para código que irá interagir com fontes externas. Certifique-se de proteger as chamadas para execde ataques externos em potencial que tentam executar código arbitrário.