Сохранить среду вспомогательного сценария Python в основном сценарии - PullRequest
0 голосов
/ 29 июня 2018

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

Class Stuff():
    def __init__(self, f):
        self.f = f
        self.log = {}

    def execute(self, filename):
        execfile(filename)

if __name__ == '__main__':
    #start this script as server
    clazz = Stuff()
    #here helper_script name will be provided by client at runtime
    clazz.execute(helper_script)

Теперь клиент будет вызывать этот вспомогательный сценарий, передавая его имя основному сценарию (серверу). После выполнения я хочу сохранить переменные вспомогательного сценария (т.е. a, b) в основном сценарии. Я знаю, один из способов - вернуть эти переменные из вспомогательного сценария в основной. Но есть ли другой способ сохранить все переменные вспомогательного скрипта. Вот как выглядит вспомогательный скрипт:

import os
a = 3
b = 4

Я пытался использовать execfile и подпроцесс.

Ответы [ 4 ]

0 голосов
/ 11 июля 2018

Вы можете получить доступ ко всем символам (переменным, классам, методам ...), которые вы определили в любом данном пространстве имен, используя метод locals(). Вот пример

import os
a = 1
b = 2
return locals()

приведет к диктату, содержащему (среди некоторых технических вещей)

{'os': <module 'os' from '...'>, 'a': 1, 'b': 2}

Таким образом, если вы сохранили вышеупомянутое в файле с именем foo.py, вы можете работать с этим значением следующим образом:

foo_vals = execfile(`foo.py`)
print(foo_vals["a"])
> 1

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

  1. Поместите ваш код в класс, который вы импортируете, а затем инициализируйте и запустите по своему усмотрению, сохраняя все соответствующие переменные в пространстве имен этого класса.
  2. Вы можете позвонить import foo, а затем получить доступ к a и b с помощью foo.a, foo.b. (Вы можете вызвать import foo в любом месте вашего кода, хотя я думаю, что он будет выполняться только один раз).
  3. Явно верните переменные в вашем скрипте, которые вам нужны, затем выполните их с помощью execfile
0 голосов
/ 04 июля 2018

Вы можете передать словарь местных жителей на execfile. После выполнения файла этот словарь будет содержать определенные локальные переменные.

class Stuff():
    def __init__(self):
        self.log = {}

    def execute(self, filename):
        client_locals = {}
        execfile(filename, globals(), client_locals)
        return client_locals

if __name__ == '__main__':
    #start this script as server
    clazz = Stuff()
    #here helper_script name will be provided by client at runtime
    client_locals = clazz.execute('client.py')
    print client_locals

С вашим client.py будет напечатано:

{'a': 3, 'b': 4, 'os': <module 'os' from '/Users/.../.pyenv/versions/2.7.6/lib/python2.7/os.pyc'>}

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

0 голосов
/ 09 июля 2018

@ Daniel Hepper Спасибо за ясное объяснение. В случае с Python3.x execfile не поддерживается. Вы можете сослаться на https://stackoverflow.com/a/437857/1790603

0 голосов
/ 04 июля 2018

Как уже упоминалось в комментариях, вы должны return значения из вспомогательного скрипта. Если их несколько, добавьте их в список или словарь (для этого удобно использовать namedtuple).

Ключ в том, что вам нужно присвоить результат execfile переменной в скрипте сервера. Это не должно быть что-то серьезное, его можно просто выбросить прямо в коллекцию, такую ​​как список.

Код на основе возврата

Основной файл

class Stuff():
    def __init__(self, f):
        self.f = f
        self.log = {}
        self.storage = []

    def execute(self, filename):
        self.storage.append(execfile(filename))

if __name__ == '__main__':
    #start this script as server
    clazz = Stuff()
    #here helper_script name will be provided by client at runtime
    clazz.execute(helper_script)

Вспомогательный файл (helper.py)

a = 3
b = 4
return (a, b)

Стандартный вариант

Альтернативный подход заключается в том, чтобы вспомогательный файл печатал результаты, но перенаправлял stdout. Это довольно тривиально, если вы используете subprocess и подробно описано в документации (могу добавить пример, если это будет полезно). Я менее знаком с подходом exec / execfile, чем subprocess, но есть объяснение этого SO вопроса .

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

Фредерик Хамиди

    code = """
i = [0,1,2]
for j in i :
print j
"""

from cStringIO import StringIO
old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
exec(code)
sys.stdout = old_stdout

print redirected_output.getvalue()

Йохен Ритцель

import sys
import StringIO
import contextlib

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO.StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

code = """
i = [0,1,2]
for j in i :
    print j
"""
with stdoutIO() as s:
    exec code

print "out:", s.getvalue()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...