Python: как передать аргументы функции __code__ функции?
Следующие работы:
def spam():
print "spam"
exec(spam.__code__)
спам
Но что, если spam
принимает аргументы?
def spam(eggs):
print "spam and", eggs
exec(spam.__code__)
TypeError: spam() принимает ровно 1 аргумент (задано 0)
Учитывая, что у меня нет доступа к самой функции, но только к объекту кода, как я могу передать аргументы объекту кода при его выполнении? Возможно ли это с помощью eval?
Изменить: поскольку большинство читателей склонны не верить в полезность этого, см. следующий прецедент:
Я хочу сохранить небольшие функции Python в файл, чтобы их можно было вызвать, например. с другого компьютера. (Излишне говорить здесь, что этот usecase строго ограничивает возможные функции.) Расселение самого объекта функции не может работать, потому что это только сохраняет имя и модуль, где определена функция. Вместо этого я мог бы выбрать __code__
функции. Когда я снова распечатаю его, конечно, ссылка на функцию исчезла, поэтому я не могу назвать функцию. Я просто не имею его во время выполнения.
Еще одна утилита:
Я работаю над несколькими функциями в одном файле, которые вычисляют некоторые данные и хранят их на жестком диске. Расчеты потребляют много времени, поэтому я не хочу выполнять функции каждый раз, но только тогда, когда реализация функции изменилась.
У меня есть версия этого запуска для целого модуля вместо функции. Он работает, просматривая время модификации файла, в котором реализован модуль. Но это не вариант, если у меня есть много функций, которые я не хочу разделять в отдельных файлах.
Ответы
Ответ 1
Можете ли вы изменить функцию, чтобы не принимать какие-либо аргументы? Затем переменные просматриваются с локалей/глобалов, где вы можете поставить в exec
:
>>> def spam():
... print "spam and", eggs
...
>>> exec(spam.__code__, {'eggs':'pasta'})
spam and pasta
(Почему бы просто не отправить целую функцию в виде строки? Pickle "def spam(eggs): print 'spam and', eggs"
и exec
строка (после проверки) с другой стороны.)
Ответ 2
Я полностью против использования __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__
, если хотите загрузить код во время выполнения.
Ответ 3
Я думаю, что в вашем более крупном приложении, вероятно, есть некоторые соображения по дизайну, которые могут заставить вас не заботиться об этой проблеме, например, возможно, иметь некоторую коллекцию "известных хороших и достоверных" функций, распространяемых в виде модуля, о котором знают исполнители,
Тем не менее, одно хакерское решение будет:
>>> 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
>>>
Ответ 4
Объект кода является частью функции, поэтому несколько ответов выше предлагают создать фиктивную функцию и заменить ее __code__
на codeObject
. Здесь другой способ, который позволяет избежать и отбрасывать новый __code__
:
import new
newFunction = new.function(codeObject, globals())
(Протестировано в Python 2.7, где spam.__code__
имеет имя spam.func_code
.)
Ответ 5
Я не думаю, что вы можете передать аргументы либо exec
, либо eval
, чтобы они были переданы в объект кода.
Вы можете обратиться к строковой версии exec/eval, например. exec("spam(3)")
.
Вы можете создать еще один объект кода, который связывает этот аргумент, и затем выполнить это:
def spam_with_eggs():
return spam(3)
exec(spam_with_eggs.__code__)
(Я думал, что вы также можете достичь этого с помощью functools.partial
, но не получили его для работы).
EDIT:
Прочитав ваши дополнительные объяснения, я подумал о способах восстановить правильную функцию из объекта кода. Этот простой подход работал у меня (в python2.5):
def bar():pass
bar.func_code = spam.func_code
bar(3) # yields "spam and 3"
Ответ 6
Мой метод, я делаю это более красивым
def f(x):
print(x, x+1)
g = type(f)(f.__code__, globals(), "optional_name")
g(3)