Как вы заставляете Python записывать код функции, имеющейся в памяти? - PullRequest
4 голосов
/ 30 декабря 2008

Когда я передаю параметры в программе (эксперимент по вычислительной биологии), я обычно передаю их через файл .py.
Итак, у меня есть этот .py файл, который выглядит так:

starting_length=9
starting_cell_size=1000
LengthofExperiments=5000000

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

def writeoptions(directory):
    options=""
    options+="starting_length=%s%s"%(starting_length,os.linesep)
    options+="starting_cell_size=%s%s"%(starting_cell_size,os.linesep)
    options+="LengthofExperiments=%s%s"%(LengthofExperiments,os.linesep)
...
    open("%s%soptions.py"%(directory,os.sep),'w').write(options)

Я хочу передать функцию в качестве одного из параметров:

starting_length=9
starting_cell_size=1000
LengthofExperiments=5000000

def pippo(a,b):
    return a+b
functionoperator=pippo

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

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

Как заставить Python записывать код функции, когда она записывает значение переменной?

Ответы [ 5 ]

13 голосов
/ 30 декабря 2008
vinko@mithril$ more a.py

def foo(a):
  print a

vinko@mithril$ more b.py

import a
import inspect

a.foo(89)
print inspect.getsource(a.foo)

vinko@mithril$ python b.py
89
def foo(a):
  print a

2 голосов
/ 30 декабря 2008

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

alias run_py='svn ci -m "Commit before running"; python2.5 $*'

и внутри скрипта, для выходного файла будет указан префикс текущей версии подрывной версии для этого файла, у вас будет запись каждого скрипта, который был запущен, и ввод. Вы можете вытащить это обратно из Subversion, если потребуется.

Другим, существенно менее полнофункциональным, средством отслеживания ввода в функцию может быть что-то вроде LodgeIt , вставка, которая принимает ввод XML-RPC и поставляется с привязками Python. (Он может быть установлен локально и имеет поддержку для ответа и обновления существующих паст.)

Но, если вы ищете относительно небольшой объем кода для включения, решение Vinko, использующее inspect, должно работать достаточно хорошо. Даг Хеллман рассказал о inspect модуле в своей серии Python Module of Week . Вы можете создать декоратор, который проверяет каждый параметр и аргумент, а затем распечатывает их соответствующим образом (я буду использовать inspect.getargspec, чтобы получить имена аргументов.)

import inspect
from functools import wraps

def option_printer(func):
    @wraps(func)
    def run_func(*args, **kwargs):
        for name, arg in zip(inspect.getargspec(func)[0], args) \
                       + sorted(kwargs.items()):
            if isinstance(arg, types.FunctionType): 
                print "Function argument '%s' named '%s':\n" % (name, func.func_name)
                print inspect.getsource(func)
            else:
                print "%s: %s" % (name, arg)
        return func(*args, **kwargs)
    return run_func

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

0 голосов
/ 30 декабря 2008

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

Я бы предложил рассмотреть более стандартный способ обработки того, что вы называете options . Например, вы можете использовать модуль JSON и сохранять или восстанавливать ваши данные. Или посмотрите на маршал и рассол модулей.

0 голосов
/ 30 декабря 2008

Хотя можно делать то, что вы просите (как показала Винко), я бы сказал, что оно чище для кода обмена . Поместите pippo и его друзей в подмодуль, к которому могут обращаться обе программы.

0 голосов
/ 30 декабря 2008

Вы спрашиваете об этом?

def writeoptions(directory):
    options=""
    options+="starting_length=%s%s"%(starting_length,os.linesep)
    options+="starting_cell_size=%s%s"%(starting_cell_size,os.linesep)
    options+="LengthofExperiments=%s%s"%(LengthofExperiments,os.linesep)
    options+="def pippo(a,b):%s" % ( os.linesep, )
    options+="    '''Some version of pippo'''%s" % ( os.linesep, )
    options+="    return 2*a+b%s" % ( os.linesep, )
    open("%s%soptions.py"%(directory,os.sep),'w').write(options)

Или что-то еще?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...