В Python при поиске неопределенной глобальной переменной возможно ли динамически генерировать значение? - PullRequest
0 голосов
/ 24 октября 2018

В Lua глобальные переменные хранятся в таблице с именем _G.Вы можете добавить метатаблицу к _G, чтобы при поиске неопределенного глобального значения вызывалась пользовательская функция для предоставления значения.

В приведенном ниже примере поиск любой неопределенной переменной возвращает имянеопределенной переменной.

setmetatable ( _G, { __index = function ( t, k ) return k end } )

print ( foo )    --  prints the string "foo"
foo  =  5
print ( foo )    --  prints 5
print ( bar )    --  prints the string "bar"

Можно ли добиться того же эффекта в Python 3?

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Обновленный ответ от 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__() в модуле.Однако, насколько я могу судить, ни один из этих методов не может подключиться к поиску глобальных переменных.Источники:

0 голосов
/ 24 октября 2018

Вы имеете в виду что-то вроде

print(bar) if 'bar' in globals() else print('bar')
...