Обновленный ответ от 12 декабря 2018 года:
Вот еще один (лучше?) Способ.В основном мы вручную read()
, compile()
, а затем exec()
весь исходный файл.Когда мы вызываем exec()
, мы можем передать альтернативный глобальный словарь.Весь файл читается, компилируется и выполняется дважды, но мы достигаем желаемого результата.
def baz ():
global foo
print ( foo ) # should print the string 'foo'
foo = 5
print ( foo ) # should print 5
print ( bar ) # should print the string 'bar'
class CustomGlobals ( dict ):
def __getitem__ ( self, k ):
if k in self: return self .get ( k )
if hasattr ( self .get ( '__builtins__' ), k ):
# we raise KeyError to avoid clobbering builtins
raise KeyError
return k
def main ():
with open ( __file__, 'rb' ) as f:
source = f .read() # re-read __file__
code = compile ( source, __file__, 'exec' ) # re-compile __file__
g = CustomGlobals ( globals() )
g [ '__name__' ] = None # prevent infinite recursion
exec ( code, g ) # re-exec __file__ against g
g [ 'baz' ] () # call the re-complied baz function
if __name__ == '__main__': main()
Приведенный выше подход является лучшим (IMO), поскольку нам не нужно вкладывать код в строку, ипотому что сообщения об ошибках будут содержать правильные номера строк.
Оригинальный ответ от 29 ноября 2018 года:
Если рассматриваемый код Python происходит из строки (а не из стандартного.py
file), тогда вы можете exec()
строку и предоставить настраиваемый глобальный словарь следующим образом:
code = '''
print ( foo ) # should print the string 'foo'
foo = 5
print ( foo ) # should print 5
print ( bar ) # should print the string 'bar'
'''
class CustomDict ( dict ):
def __init__ ( self, other ): super() .__init__ ( other )
def __getitem__ ( self, k ):
if k in self: return self .get ( k )
if hasattr ( self .get ( '__builtins__' ), k ):
# we raise KeyError to avoid clobbering builtins
raise KeyError
return k
exec ( code, None, CustomDict ( globals() ) )
По желанию вышеприведенные выводы:
foo
5
bar
У меня естьНе найдено никакого способа «мутировать» модуль для достижения того же результата для кода, который находится в этом модуле.Если кто-нибудь знает способ изменить модуль, я был бы очень рад его увидеть.
(Возможно, модуль мог бы прочитать свой собственный исходный код в строку, а затем скомпилировать эту строку в контексте пользовательскогоглобальный dict
, а затем вставить результат в sys.modules
? Другими словами, модуль заменит себя на клон самого себя, но с другим глобальным dict
. Хммм.)
В сторону:Существуют способы имитации __getitem__()
и __getattr__()
на модуле.Начиная с Python 3.7, вы можете просто и прямо определить функцию __getattr__()
в модуле.Однако, насколько я могу судить, ни один из этих методов не может подключиться к поиску глобальных переменных.Источники: