Python: Как передать аргументы в __code__ функции? - PullRequest
23 голосов
/ 03 мая 2011

работают следующие работы:

def spam():
    print "spam"
exec(spam.__code__)

спам

Но что, если spam принимает аргументы?

def spam(eggs):
    print "spam and", eggs
exec(spam.__code__)

TypeError: spam () принимает ровно 1 аргумент (задано 0)

Учитывая, что у меня нет доступа к самой функции, а есть только к объекту кода, как я могу передать аргументы объекту кода при его выполнении? Это возможно с eval?

Редактировать: так как большинство читателей склонны не верить в полезность этого, см. Следующий вариант использования:

Я хочу сохранить небольшие функции Python в файл, чтобы их можно было вызывать, например, с другого компьютера. (Нет необходимости говорить, что этот вариант использования строго ограничивает возможные функции.) Выбор самого объекта функции не может работать, потому что это только сохраняет имя и модуль, в котором определена функция. Вместо этого я мог выбрать __code__ функции. Когда я снова его открываю, ссылка на функцию исчезла, поэтому я не могу вызвать функцию. У меня его просто нет во время выполнения.

Другой вариант использования:

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

У меня есть версия этого для всего модуля вместо функции. Он работает, просматривая время изменения файла, в котором реализован модуль. Но это не вариант, если у меня много функций, которые я не хочу разделять в отдельные файлы.

Ответы [ 6 ]

15 голосов
/ 03 мая 2011

Я полностью против этого использования __code __.

Хотя я любопытный человек, и это то, что кто-то теоретически может сделать:

code # This is your code object that you want to execute

def new_func(eggs): pass
new_func.__code__ = code
new_func('eggs')

Опять же, я никогда не хочу видеть это использованным, никогда. Возможно, вы захотите заглянуть в __import__, если хотите загрузить код во время выполнения.

9 голосов
/ 03 мая 2011

Можете ли вы изменить функцию на не принимать какие-либо аргументы?Затем переменные ищутся из локальных / глобальных переменных, где вы можете указать в exec:

>>> def spam():
...   print "spam and", eggs
... 
>>> exec(spam.__code__, {'eggs':'pasta'})
spam and pasta

(Почему бы просто не отправить всю функцию в виде строки? Pickle "def spam(eggs): print 'spam and', eggs" и execстрока (после проверки) на другой стороне.)

6 голосов
/ 03 мая 2011

Я думаю, что в вашем более крупном приложении, вероятно, есть некоторые соображения, связанные с дизайном, которые могут не заботиться об этой проблеме, например, возможно, имеется некоторая коллекция «известных хороших и действительных» функций, распределенных в виде модуля, о котором знают исполняющие агенты, или что-то в этом роде.,

Тем не менее, одно хакерское решение было бы:

>>> def spam(eggs):
...     print "spam and %s" % eggs
...     
... 
>>> spam('bacon')
spam and bacon
>>> def util():
...     pass
...     
... 
>>> util.__code__ = spam.__code__
>>> util('bacon')
spam and bacon
>>> 
5 голосов
/ 03 марта 2013

Объект кода является частью функции, поэтому в нескольких ответах выше предлагается создать фиктивную функцию и заменить ее __code__ на codeObject.Вот еще один способ избежать создания и выбрасывания нового __code__:

import new
newFunction = new.function(codeObject, globals())

(протестировано в Python 2.7, где spam.__code__ названо spam.func_code.)

1 голос
/ 01 августа 2011

Мой метод, мне кажется, он красивее

def f(x):
    print(x, x+1)

g = type(f)(f.__code__, globals(), "optional_name")

g(3)
1 голос
/ 03 мая 2011

Я не думаю, что вы можете передавать аргументы либо exec, либо eval, чтобы они передавались объекту кода.

Вы можете прибегнуть к строковой версии exec / eval,например, exec("spam(3)").

Вы можете создать другой объект кода, который связывает аргумент, и затем выполнить это:

def spam_with_eggs():
   return spam(3)
exec(spam_with_eggs.__code__)

(я думал, что вы также можете достичь этого с помощью functools.partial, ноне заставил его работать).

РЕДАКТИРОВАТЬ:

После прочтения ваших дополнительных объяснений я подумал о способах восстановления правильной функции из объекта кода.Этот простой подход работал для меня (в python2.5):

def bar():pass
bar.func_code = spam.func_code
bar(3)  # yields "spam and 3"
...