Изменить параметры скрипта Python из другого скрипта Python - PullRequest
0 голосов
/ 24 марта 2020

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

main.py может выглядеть так:

import NumPy as np
import module
import bar

...

foo = bar(label='C2H4', point=(1,0))

atoms = 'H4'
template = read('template.t')
size = template.lengths()

n = 4
alpha = 0.5
batch_size = 256 // (n * alpha)

dct = {
    '1': [1, 2],
    '6': [3, 4],
}

kwargs = {
    'dict': dct,
    'size': size,
    'scale': size[0] / 10,
}

...

module(atoms, kwargs, foo)
module.run()

В другом сценарии, называемом parameter_search.py, я делаю копию и изменяю параметры, выполняя каждую строку в main.py в поисках того, что следует изменить.

Если переменная найдена, a Команда regex разбивает строку, а затем изменяет число с плавающей точкой (я не лучший в регулярных выражениях, так что, возможно, это может потребовать некоторой работы):

import re
import fileinput
import os from shutil import copyfile

def is_num(var):
    try:
        float(var)
        return True
    except ValueError:
        return False

def replace(filename, var_expr, new_val):
    found = False
    for line in fileinput.input(filename, inplace=1):
        if var_expr in line:
            if not found:
                found = True
                lst = re.split('(=|:|,)', line)
                for i, char in enumerate(lst):
                    if is_num(char):
                        lst[i] = str(new_val)
                line = "".join(lst)
            else:
                raise NameError(f'{var_expr} ambigous')
        print(line, end="")
    if not found:
        raise NameError(f'{var_expr} not found in {filename}')


N = [10, 20]
alpha = [0.4, 0.6, 0.8]
foo = [bar(alpha=1), baz()]

for n in N:
    for a in alpha:
        for i, f in enumerate(foo):
            newfile = 'main'
            newfile += f'_n{n}'
            newfile += f'_a{a}'
            newfile += f'_f{i}'
            newfile += '.py'

            copyfile('main.py', newfile)

            replace(newfile, 'n=', n)
            replace(newfile, 'alpha=', use_n)
            replace(newfile, 'foo = ', f)

Это дает приличные результаты, но проблемы возникают, если несколько переменные находятся в одной строке, например bar(label='C2H4', point=(1, 0)), или переменная является частью dict как kwargs, параметр является строкой, функцией или какой-то другой странной переменной.

Возможно ли сделать что-то вроде replace(), что является более общим или каким-то другим образом делает это возможным?

1 Ответ

1 голос
/ 24 марта 2020

Хорошо, позвольте мне сделать несколько предположений:

  1. Назначение параметров в сценарии .py происходит только один раз, и это также первый случай появления имени параметра.
  2. Существует ровно два способа объявления параметра: в виде одной переменной par = value или внутри словаря {'par' : value} (с одинарными или двойными кавычками).

Затем можно использовать re.sub функция для прямой замены значения присваивания с использованием так называемых групп захвата :

re.sub(pattern, repl, string, count=0, flags=0)

Возвращает строку, полученную путем замены крайних левых непересекающихся вхождений шаблона в строке при замене репл. Если шаблон не найден, строка возвращается без изменений. repl может быть строкой или функцией; если это строка, обрабатывается любое экранирование backsla sh. То есть \ n преобразуется в один символ новой строки, \ r преобразуется в возврат каретки и т. Д. Неизвестные экранированные буквы ASCII зарезервированы для будущего использования и рассматриваются как ошибки. Другие неизвестные побеги, такие как \ &, остаются одни. Обратные ссылки, такие как \ 6, заменяются подстрокой, сопоставленной группе 6 в шаблоне .

source: https://docs.python.org/3/library/re.html

Итак, ваша функция для установки нового значения для данного имени параметра может выглядеть следующим образом:

def replace_parameter_value_if_found(string, parameter, new_value):
    return new_string = re.sub("([\'\"]?"+parameter+"[\'\"]?\s*[=:]\s*)[\.\w]*", "\1"+new_value, string)

Теперь давайте разберем его:

"([\'\"]?"+parameter+"[\'\"]?\s*[=:]\s*)[.\w]*"
  • Материал заключенный в () называется группой захвата - на него можно ссылаться позже.
  • Материал, заключенный в [], соответствует любому из этих символов внутри (обратная черта sh служит в качестве escape-символа)
  • ? - это квантификатор, совпадающий с нулем или одним вхождением того, что он немедленно добивается
  • * - это квантификатор, сопоставляющий любое количество вхождений того, что он немедленно добивается, включая ноль
  • любая буквенная строка соответствует этой строке
  • \s и \w соответствуют любому пробелу и любому символу слова (a-z0-9) соответственно

Итак, скажем, параметр был 'alpha', шаблон регулярного выражения становится

"([\'\"]?alpha[\'\"]?\s*[=:]\s*)[\.\w]*"

и читается Вот как:

  1. Открытая группа захвата 1
  2. Соответствует одиночному «или» или ни одному (из-за?)
  3. Соответствует буквальному слову alpha
  4. Соответствует одиночному символу 'или' или ни одному
  5. Соответствует любому количеству пробелов
  6. Соответствует одиночному = или:
  7. Закрыть группу захвата 1 ( теперь он содержит alpha= или 'alpha': или какой-либо его вариант)
  8. Соответствует любому количеству символов или точек слова (это то, что мы будем заменять)

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

"\1"+new_value

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

Надеюсь, это поможет.

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