Как работают крышки в runpy? - PullRequest
3 голосов
/ 17 августа 2011

Я получаю неожиданное поведение при попытке запустить методы, определенные в файле, загруженном с использованием модуля runpy . Методы не видят никаких переменных (включая импортированные модули), определенных вне этого метода. Вот как я это делаю:

#test.py
import runpy
env = runpy.run_path('test', {'y':'world'})
env['fn']()

~

#test
import re

print(re.compile(r'^hello', re.IGNORECASE).sub('', "hello world"))
x = "hello"
print(x)
print(y)

def fn():
    try:
        print(re.compile(r'^hello', re.IGNORECASE).sub('', "hello world"))
    except:
        print("No re")
    try:
        print(x)
    except:
        print("No x")
    try:
        print(y)
    except:
        print("No y")

Мой ожидаемый результат test.py будет:

 world
hello
world
 world
hello
world

потому что fn сформирует замыкание для re, x и y.

Однако вместо этого я получаю:

 world
hello
world
No re
None
None

Похоже, что re не определен в fn, хотя это должно быть при обычном поведении закрытия x и y еще более странные, потому что они определены, но имеют значение None.

Почему это так и как закрытия работают с runpy? Как мне добиться нормального поведения, чтобы fn мог «видеть» внешние переменные?

1 Ответ

4 голосов
/ 17 августа 2011

ОК, это любопытно, как Python обрабатывает модули, о которых я знаю, но не до конца понимаю.Я сталкивался с этим во время работы над IPython, где это объясняется в комментарии .

Когда Python запускает модуль, он создает объект модуля, атрибутами которого являются глобальные именав модуле.Когда модуль выходит из области видимости и уничтожается, эти атрибуты устанавливаются в None.Код, который был определен в функции, воспринимает их как глобальные, как вы нашли.Вы можете продемонстрировать это, добавив def g(): return globals() к своему файлу, а затем позвонив env["g"]().

Я не знаю, есть ли способ обойти это с runpy.IPython использует некоторый сложный код для повторного использования объекта модуля для запуска других файлов, кэшируя копии своего __dict__, чтобы сохранить ссылки в нем живыми.Посмотрите на magic_run функцию , если вам интересно.

...