Как разделить переменные между скриптами в python? - PullRequest
18 голосов
/ 02 декабря 2009

следующее не работает

one.py

import shared
shared.value = 'Hello'
raw_input('A cheap way to keep process alive..')

two.py

import shared
print shared.value

запускается в двух командных строках как:

>>python one.py
>>python two.py

(второй получает ошибку атрибута, правильно).

Есть ли способ сделать это, то есть разделить переменную между двумя сценариями?

Ответы [ 10 ]

37 голосов
/ 05 февраля 2013

Надеюсь, можно записать мои заметки об этой проблеме здесь.

Прежде всего, я очень ценю пример в OP, потому что именно с этого я и начал - хотя он заставил меня думать, shared - это какой-то встроенный модуль Python, пока я не нашел полный пример в [Tutor] Глобальные переменные между модулями ?? .

Однако, когда я искал «совместное использование переменных между сценариями» (или процессами) - кроме случая, когда сценарию Python необходимо использовать переменные, определенные в других исходных файлах Python (но не обязательно при запуске процессов) - я в основном наткнулся на два другие случаи использования:

  • Сценарий разветвляется на несколько дочерних процессов, которые затем выполняются параллельно (возможно, на нескольких процессорах) на одном компьютере
  • Сценарий порождает несколько других дочерних процессов, которые затем выполняются параллельно (возможно, на нескольких процессорах) на одном компьютере

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

Однако меня интересует выполнение нескольких вызовов одного и того же сценария, выполняемых независимо, и совместное использование данных между ними (как в Python: как разделить экземпляр объекта между несколькими вызовами сценария ), в режиме одного экземпляра. Эта проблема на самом деле не решается в двух вышеупомянутых случаях - вместо этого она сводится к примеру в OP (разделение переменных между двумя сценариями).

Теперь при решении этой проблемы в Perl существует IPC :: Shareable ; который «позволяет привязать переменную к общей памяти», используя «целое число или строку из 4 символов [1], которая служит общим идентификатором для данных в пространстве процесса». Таким образом, нет ни временных файлов, ни сетевых настроек - что я считаю отличным для моего варианта использования; поэтому я искал то же самое в Python.

Однако, как принял ответ от @ Drewfer отмечает: " Вы не сможете делать то, что хотите, без хранения информации где-то за пределами двух экземпляров интерпретатора"; или другими словами: либо вы должны использовать настройку сети / сокета, либо вы должны использовать временные файлы (следовательно, нет общей оперативной памяти для " полностью отдельных сеансов Python ").

Теперь, даже с учетом этих соображений, довольно трудно найти рабочие примеры (за исключением pickle) - также в документах для mmap и многопроцессорной обработки . Мне удалось найти некоторые другие примеры, которые также описывают некоторые подводные камни, которые не упоминаются в документации:

Благодаря этим примерам я придумал пример, который, по сути, делает то же самое, что и пример mmap, с подходами из примера " синхронизируют python dict " - с использованием BaseManager ( через manager.start() через адрес пути к файлу) с общим списком; и сервер, и клиент читают и пишут (вставлено ниже). Обратите внимание, что:

  • multiprocessing менеджеры могут быть запущены через manager.start() или server.serve_forever()
    • serve_forever() замки - start() не
    • В multiprocessing есть функция автоматической регистрации: кажется, что она отлично работает с start() редактируемыми процессами - но, похоже, игнорирует те, которые serve_forever()
  • Спецификация адреса в multiprocessing может быть IP-адресом (сокетом) или временным файлом (возможно, каналом?); в multiprocessing документах:
    • В большинстве примеров используется multiprocessing.Manager() - это просто функция (, а не экземпляр класса), которая возвращает SyncManager, который является специальным подклассом BaseManager; и использует start() - но не для IPC между независимо запускаемыми сценариями; здесь используется путь к файлу
    • Несколько других примеров serve_forever() подход для IPC между независимо выполняемыми сценариями; здесь используется IP / адрес сокета
    • Если адрес не указан, то путь к временному файлу используется автоматически (см. 16.6.2.12. Ведение журнала для примера того, как это увидеть)

В дополнение ко всем подводным камням в посте " синхронизация python dict ", есть и дополнительные в случае списка. Этот пост отмечает:

Все манипуляции с dict должны выполняться с помощью методов, а не присвоений dict (syncdict ["blast"] = 2 с треском провалится из-за того, что многопроцессорная обработка разделяет собственные объекты)

Обходной путь для dict['key'] получения и настройки - использование dict открытых методов get и update. Проблема в том, что нет таких открытых методов как альтернатива для list[index]; таким образом, для общего списка, кроме того, мы должны зарегистрировать методы __getitem__ и __setitem__ (которые являются приватными для list) как exposed, что означает, что мы также должны перерегистрировать все открытые методы для list а также :/

Ну, я думаю, это были самые важные вещи; это два сценария - их можно просто запустить в отдельных терминалах (сначала сервер); Примечание, разработанное для Linux с Python 2.7:

a.py (сервер):

import multiprocessing
import multiprocessing.managers

import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)


class MyListManager(multiprocessing.managers.BaseManager):
    pass


syncarr = []
def get_arr():
    return syncarr

def main():

    # print dir([]) # cannot do `exposed = dir([])`!! manually:
    MyListManager.register("syncarr", get_arr, exposed=['__getitem__', '__setitem__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'])

    manager = MyListManager(address=('/tmp/mypipe'), authkey='')
    manager.start()

    # we don't use the same name as `syncarr` here (although we could);
    # just to see that `syncarr_tmp` is actually <AutoProxy[syncarr] object>
    # so we also have to expose `__str__` method in order to print its list values!
    syncarr_tmp = manager.syncarr()
    print("syncarr (master):", syncarr, "syncarr_tmp:", syncarr_tmp)
    print("syncarr initial:", syncarr_tmp.__str__())

    syncarr_tmp.append(140)
    syncarr_tmp.append("hello")

    print("syncarr set:", str(syncarr_tmp))

    raw_input('Now run b.py and press ENTER')

    print
    print 'Changing [0]'
    syncarr_tmp.__setitem__(0, 250)

    print 'Changing [1]'
    syncarr_tmp.__setitem__(1, "foo")

    new_i = raw_input('Enter a new int value for [0]: ')
    syncarr_tmp.__setitem__(0, int(new_i))

    raw_input("Press any key (NOT Ctrl-C!) to kill server (but kill client first)".center(50, "-"))
    manager.shutdown()

if __name__ == '__main__':
  main()

b.py (клиент)

import time

import multiprocessing
import multiprocessing.managers

import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)


class MyListManager(multiprocessing.managers.BaseManager):
    pass

MyListManager.register("syncarr")

def main():
  manager = MyListManager(address=('/tmp/mypipe'), authkey='')
  manager.connect()
  syncarr = manager.syncarr()

  print "arr = %s" % (dir(syncarr))

  # note here we need not bother with __str__ 
  # syncarr can be printed as a list without a problem:
  print "List at start:", syncarr
  print "Changing from client"
  syncarr.append(30)
  print "List now:", syncarr

  o0 = None
  o1 = None

  while 1:
    new_0 = syncarr.__getitem__(0) # syncarr[0]
    new_1 = syncarr.__getitem__(1) # syncarr[1]

    if o0 != new_0 or o1 != new_1:
      print 'o0: %s => %s' % (str(o0), str(new_0))
      print 'o1: %s => %s' % (str(o1), str(new_1))
      print "List is:", syncarr

      print 'Press Ctrl-C to exit'
      o0 = new_0
      o1 = new_1

    time.sleep(1)


if __name__ == '__main__':
    main()

Как последнее замечание, в Linux создается /tmp/mypipe - но это 0 байтов и имеет атрибуты srwxr-xr-x (для сокета); Я думаю, это меня радует, так как мне не нужно беспокоиться ни о сетевых портах, ни о временных файлах как таковых :)

Другие связанные вопросы:

17 голосов
/ 02 декабря 2009

Вы не сможете делать то, что хотите, не сохраняя информацию где-то за пределами двух экземпляров интерпретатора.
Если вам нужны только простые переменные, вы можете легко вывести команду python dict в файл с модулем pickle в первом сценарии, а затем повторно загрузить его во втором сценарии. Пример:

one.py

import pickle

shared = {"Foo":"Bar", "Parrot":"Dead"}
fp = open("shared.pkl","w")
pickle.dump(shared, fp)

two.py

import pickle

fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]
11 голосов
/ 10 октября 2010

sudo apt-get установить memcached python-memcache

one.py

import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)
shared.set('Value', 'Hello')

two.py

import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)    
print shared.get('Value')
6 голосов
/ 02 декабря 2009

То, что вы пытаетесь сделать здесь (сохранить общее состояние в модуле Python через отдельные интерпретаторы Python), не будет работать.

Значение в модуле может быть обновлено одним модулем, а затем прочитано другим модулем, но это должно быть в том же интерпретаторе Python. То, что вы здесь делаете, на самом деле является своего рода межпроцессным взаимодействием; это может быть достигнуто с помощью сокетной связи между двумя процессами, но это значительно менее тривиально, чем то, что вы ожидаете получить здесь.

5 голосов
/ 27 января 2015

вы можете использовать относительный простой файл mmap. Вы можете использовать shared.py для хранения общих констант. Следующий код будет работать с разными интерпретаторами python \ scripts \ process

shared.py:

MMAP_SIZE = 16*1024 
MMAP_NAME = 'Global\\SHARED_MMAP_NAME'

* «Глобальный» - это синтаксис Windows для глобальных имен

one.py:

from shared import MMAP_SIZE,MMAP_NAME                                                        
def write_to_mmap():                                                                          
    map_file = mmap.mmap(-1,MMAP_SIZE,tagname=MMAP_NAME,access=mmap.ACCESS_WRITE)             
    map_file.seek(0)                                                                          
    map_file.write('hello\n')                                                                 
    ret = map_file.flush() != 0                                                               
    if sys.platform.startswith('win'):                                                        
        assert(ret != 0)                                                                      
    else:                                                                                     
        assert(ret == 0)                                                                      

two.py:

from shared import MMAP_SIZE,MMAP_NAME                                          
def read_from_mmap():                                                           
    map_file = mmap.mmap(-1,MMAP_SIZE,tagname=MMAP_NAME,access=mmap.ACCESS_READ)
    map_file.seek(0)                                                            
    data = map_file.readline().rstrip('\n')                                     
    map_file.close()                                                            
    print data                                                                  

* Этот код был написан для Windows, Linux может потребоваться небольшая корректировка

подробнее на - https://docs.python.org/2/library/mmap.html

3 голосов
/ 02 декабря 2009

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

Из примеров документа:

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print q.get()    # prints "[42, None, 'hello']"
    p.join()
3 голосов
/ 02 декабря 2009

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

Модуль pickle и cPickle может сохранять и загружать большинство объектов python в файл.

Модуль shelve может хранить объекты python в структуре, подобной словарю (используя pickle за кулисами).

Модули dbm / bsddb / dbhash / gdm могут хранить строковые переменные в структуре, подобной словарю.

Модуль sqlite3 может хранить данные в облегченной базе данных SQL.

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

1 голос
/ 02 декабря 2009

В вашем примере первый скрипт выполняется до конца, а затем запускается второй. Это означает, что вам нужно какое-то постоянное состояние. Другие ответы предлагали использовать текстовые файлы или модуль Python pickle. Лично я ленивый, и я бы не использовал текстовый файл, когда мог бы использовать pickle; Почему я должен написать анализатор для анализа моего собственного формата текстового файла?

Вместо pickle вы также можете использовать модуль json для сохранения его в формате JSON. Это может быть предпочтительнее, если вы хотите поделиться данными с программами, не относящимися к Python, поскольку JSON является простым и распространенным стандартом. Если ваш Python не имеет json, получите simplejson .

Если ваши потребности выходят за рамки pickle или json - скажем, вы действительно хотите, чтобы две программы Python выполнялись одновременно и обновляли постоянные переменные состояния в реальном времени - я предлагаю вам использовать База данных SQLite . Используйте ORM для абстрагирования базы данных, и это очень просто. Для SQLite и Python я рекомендую Осенний ORM .

1 голос
/ 02 декабря 2009

Используйте текстовые файлы или переменные окружения. Так как они работают раздельно, вы не можете делать то, что пытаетесь сделать.

0 голосов
/ 10 апреля 2019

Использование Redis для совместного использования динамической переменной:

script_one.py

from redis import Redis
form time import sleep

cli = Redis('localhost')
shared_var = 0

while True:
   cli.set('share_place', shared_var)
   share_var += 1
   sleep(1)

Запуск script_one в терминале (процесс):

$ python script_one.py

script_two.py

from time import sleep
from redis import Redis

cli = Redis('localhost')

while True:
    print(int(cli.get('share_place')))
    sleep(1)

Запуск script_two в другом терминале (другой процесс):

$ python script_two.py

Из:

0
1
2
3
4
...

Зависимость:

$ pip install redis
$ apt-get install redis-server
...