LINUX.ORG.RU

Как вызвать функцию с не-родным global namespace?

 ,


0

1

Есть какая то функция пользователя, скажем

def f(): return a+1    

Я хочу ее вызвать так, что бы при вызове у нее глобальное пространство имен, откуда тянется переменная a, было не родным (того модуля где определена f) а каким то моим. В качестве альтернативы пойдет повесить как то на замыкание f мое пр-во имен.

В общем хочется, что бы при вызове функции неизвестные переменные в коде ф-ии тянулись из моего пр-ва имен (объекта, эмулирующего словарь).

★★★★★
eval(...)
    eval(source[, globals[, locals]]) -> value
    
    Evaluate the source in the context of globals and locals.
    The source may be a string representing a Python expression
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.
tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Не-а. Есть тонкая тонкость - eval и exec позволяют менять намеспайсы для выражения. Если в выражении стоит 'f()' то для самой f намеспайс останется какой был. Если только тело f загнать в exec - так я как раз этого делать не хочу (откуда я тело ф-ии возьму), а exec(f.func_code) ведет себя странно и не позвляет наймспейсы менять...

AIv ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Дык в том то все и дело, что я вместо глобал наймеспайсе хочу подсунуть свой объект, который довольно специфически работает. Так как Вы предлагаете этого точно сделать не получиться...

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Дык в том то все и дело, что я вместо глобал наймеспайсе хочу подсунуть свой объект, который довольно специфически работает.

Развернуть этот объект в последовательность Python-операторов нельзя?

tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Кстати, продолжая тему «в Python ничего спрятать нельзя»:

>>> def f(): print x
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in f
NameError: global name 'x' is not defined
>>> f.func_globals["x"] = 123
>>> f()
123
>>> 
tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Этот объект генерит новое значение по ключу в зависимости от ключа. Ну скажем _2012_10_03 - дата третье октября сего года и т.д.;-)

Так что увы - заранее набить все потенциальные значения в наймспасе не получиться... хотя, в f.func_code.co_names есть все имена функции, так что это в принципе возможно.

Но таки хотелось бы сделать через эмуляцию namespace, это очень многое упростит.

AIv ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

АГа... ничего что x при этом оказывается в глобальном пр-ве имен?

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> def f(): return x
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in f
NameError: global name 'x' is not defined
>>> f.func_globals['x'] = 123
>>> f()
123
>>> x
123
>>> 

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

ничего что x при этом оказывается в глобальном пр-ве имен?

Эээ... ты у меня спрашиваешь? %) Если это неприемлемо, сделай del x после вызова функции.

tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Use FunctionType, Luke!

In [1]: def f(): return x

In [6]: from types import FunctionType

In [7]: new_env = f.func_globals.copy()

In [8]: fl = FunctionType(f.func_code, new_env, f.func_name, f.func_defaults, f.func_closure)

In [9]: fl()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/home/max/<ipython-input-9-d41bee4190f2> in <module>()
----> 1 fl()

/home/max/<ipython-input-1-4d3cc9596328> in f()
----> 1 def f(): return x

NameError: global name 'x' is not defined

In [10]: x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/home/max/<ipython-input-10-401b30e3b8b5> in <module>()
----> 1 x

NameError: name 'x' is not defined

In [11]: new_env['x'] = 42

In [12]: fl()
Out[12]: 42

In [13]: x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/home/max/<ipython-input-13-401b30e3b8b5> in <module>()
----> 1 x

NameError: name 'x' is not defined

Begemoth ★★★★★
()
Ответ на: комментарий от AIv

Вообще, подумав - хочется даже не с не-родным local namespace!

По идее это уже с изменением func_closure, но я сам такого не делал.

Begemoth ★★★★★
()
Ответ на: комментарий от tailgunner

Да, это замечательный способ что то спрятать! Я кстати могу предложить идеальную команду для защиты информации: rm -rf ... ;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от Begemoth

Блин... какая то очень скудная информация про эту func_closure... оно должны быть tuple, но чего tuple?

AIv ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Дык почистить не проблема, проблема закрыть какие то поля от внешнего доступа. Точнее и это не проблема (прятать что то в памяти своего процесса от себя же довольно странная идея), - проблема в том что бы закрыть данные хотя бы символически. Я обычно не заморачиваюсь, то что для внутреннего пользования начинается с подчеркивания.

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

проблема в том что бы закрыть данные хотя бы символически.

Задай отдельные глобалы через FunctionType (а не используй общие, как в моем примере с f.func_globals), и дело сделано. Хотя насчет «с измененным локальным namespace-ом» я просто не понял. Как звучит настоящий вопрос?

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Так примерно и звучит как ни странно. Ок, от печки:

Есть некая система занимающаяся кодогенерацией для числодробилок и еще черти чем. Там встречается код типа:

section('CCC')
a[1,-3,2] = a[0,-2,3]+b[1,3,4]*c ...
...
section('LLC')
...

Хочется поменять это на

def CCC() :
    a1_32 = a0_23+b134*c ...
...
def LLC() :
...

Гипотетически это возможно - надо тело ф-ии запустить с пользовательским объектом эмулирующим локальный намеспайс, со специального вида __getitem__/__setitem__

ВОпрос технический - КАК запустить тело ф-ии... Вариант загнать тело в виде строки в exec можно, но не хочется, т.к. теряется подсветка синтаксиса и вообще...

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Что ты вообще хочешь получить? Все переменные не определённые в точке определения функции являются глобальными, и только к глобальным переменным обращение идёт по именам, к локальным и захваченным из внешних областей видимости - по индексам (см. func.func_code.co_cellvars).

Begemoth ★★★★★
()
Ответ на: комментарий от AIv

надо тело ф-ии запустить с пользовательским объектом эмулирующим локальный намеспайс

Тебе нужно отдельное _глобальное_ пространство имён - замена func_globals, то что я показал.

Begemoth ★★★★★
()
Ответ на: комментарий от AIv

Я кстати могу предложить идеальную команду для защиты информации: rm -rf ...

открой для себя shred -uzn666

drBatty ★★
()
Ответ на: комментарий от Begemoth

Мне надо что бы в моем пр-ве имен создавались новые переменные, это только локалс. Но я понял, там с индексами ловить нечего, только exec похоже... или патчить питон;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

прятать что то в памяти своего процесса от себя же довольно странная идея

это хорошая, годная идея. Я по вашему должен count1, count2, count3... и так Over9000 count'ов делать?

drBatty ★★
()
Ответ на: комментарий от tailgunner

Ес-но валижный питон, мы за это боролись... не очень хочется связываться с exec-ом, но похоже других альтернатив нету.

AIv ★★★★★
() автор топика
Ответ на: комментарий от drBatty

Вы? По моему? По моему Вам не надо писать не по теме в треде (для начала).

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Непонятно. Если код ваш, или всё равно его предполагается переписать, что мешает переписать ту же f таким образом, чтобы она явно брала переменные не из глобального неймспейса, а из спец. объекта.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Была и такая мысль. Будет много лишних букв - этот объект каждый раз придется указывать.

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Ес-но валижный питон, мы за это боролись...

Кхм. Валидный Python - это

section('CCC')
a[1,-3,2] = a[0,-2,3]+b[1,3,4]*c ...
...
section('LLC')

его точно нельзя интерпретировать напрямую, строка за строкой?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ну во первых там есть и многострочные конструкции, во вторых если файл подымать так лучше execfile (как раз туда пр-ва имен передаются).

И в третьих Вы можете предложить вариант перехода в построчный разбор и потом обратно? Т.е. вот сунул я питону файл (как обычно), он его жует, потом хопа - меняет алгоритм разбора а потом хопа - возвращается к старому? Я че то не понял про построчный разбор...

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

в третьих Вы можете предложить вариант перехода в построчный разбор и потом обратно?

Я, наверное, чего-то не понимаю, но:

for l in file("whatever").readlines():
  if l.startswith("XXX"):
    eval(l)
tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Мне надо что бы в моем пр-ве имен создавались новые переменные, это только локалс

Ну эти же функции ты сам генеришь: добавь в конец копирование всех переменных из locals() в globals() твоей функции.

Begemoth ★★★★★
()
Ответ на: комментарий от AIv

Много букв? О_о. Сомнительно. Одна строчка, не более. И это решение хотя бы не выглядит черезжопно-универсальным как те, что представлены в треде.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Begemoth

скорее всего имеется ввиду модуль parser.

Если заходить настолько далеко, можно просто сделать трансформацию исходников и не заморачиваться с подсовыванием namespace-ов :)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

ЧЕм это лучше execfile? Чем хуже понятно - никакие многострочные конструкции не проходят.

execfile тоже не комильфо, но видать это меньшее из всех зол.

AIv ★★★★★
() автор топика
Ответ на: комментарий от Begemoth

Ну эти же функции ты сам генеришь: добавь в конец копирование всех переменных из locals() в globals() твоей функции.

Вообще то для меня имеет значение порядок создания переменных... но мысль хорошая, спасибо, я ее подумаю;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Только сейчас доперло. ТОгда лучше выбрать нужные строки и ехекнуть весь кусок. СПасибо, это наверное оптимально... только вот как узнать последнюю строку функции? Первую я знаю, f.func_code.co_firstlineno ;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

только вот как узнать последнюю строку функции?

section() или конец файла? Ну и если текст вы сами генерите, пометьте конец функции как-то.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

section()

Файл как раз не генериться а вводиться юзером, на его основе генериться С++.

ЗЫ как раз с секциями проблем нет - все работает, но код кривоват. Хочется секии заменить обычными ф-ями.

AIv ★★★★★
() автор топика
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от AIv

Хм. Я-то думал, локальные переменные f заранее известны. Тогда можно было бы просто написать в начале функции
vars().update(подготовленный_неймспейс)

Virtuos86 ★★★★★
()

Извращенной задаче - извращенное решение.

Декоратор получает код функции, разбирает на токены, затем меняет переменные на нужные вам объекты, снова собирает код и выполняет.

Belkrr
()
Ответ на: комментарий от Virtuos86

Вообще то как раз так делать НЕ хочется - переменных много.

AIv ★★★★★
() автор топика
Ответ на: комментарий от Belkrr

Да уж... месье определенно знает толк в !;-)

AIv ★★★★★
() автор топика

перепиши код чтобы не хотелось ничего заменять. Это сэкономит деньги, время и нервы.

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.