如何在 python 中跨脚本共享变量?

2025-04-10 09:46:00
admin
原创
17
摘要:问题描述:以下不起作用一.pyimport shared shared.value = 'Hello' raw_input('A cheap way to keep process alive..') 二.pyimport shared print shared.value 在两个命令行上运行:>>...

问题描述:

以下不起作用

一.py

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

二.py

import shared
print shared.value

在两个命令行上运行:

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

(第二个出现属性错误,这是正确的)。

有没有办法实现这一点,即在两个脚本之间共享一个变量?


解决方案 1:

希望我可以在这里记下我对这个问题的笔记。

首先,我非常欣赏 OP 中的例子,因为这也是我的起点 —— 尽管它让我以为shared是一些内置的 Python 模块,直到我在[Tutor] 模块之间的全局变量??中找到一个完整的例子。

然而,当我寻找“在脚本(或进程)之间共享变量”时 - 除了 Python 脚本需要使用其他 Python 源文件中定义的变量(但不一定是正在运行的进程)的情况之外 - 我主要偶然发现了另外两个用例:

  • 脚本将自身分叉为多个子进程,然后在同一台 PC 上并行运行(可能在多个处理器上)

  • 一个脚本会生成多个其他子进程,然后这些子进程在同一台 PC 上并行运行(可能在多个处理器上)

因此,大多数关于“共享变量”和“进程间通信”(IPC)的文章都讨论了类似这两种情况;然而,在这两种情况下,我们都可以观察到“父级”,而“子级”通常会引用它。

然而,我感兴趣的是运行同一脚本的多个调用,独立运行,并在单例/单实例模式下在它们之间共享数据(如Python:如何在脚本的多个调用之间共享对象实例)。 上述两种情况并没有真正解决这种问题 - 相反,它本质上归结为 OP 中的示例(在两个脚本之间共享变量)。

现在,在 Perl 中处理这个问题时,可以使用IPC::Shareable;它“允许您将变量绑定到共享内存”,使用“整数或 4 个字符串[1] 作为跨进程空间数据的公共标识符”。因此,没有临时文件,也没有网络设置 - 我发现这对我的用例非常有用;所以我在 Python 中寻找同样的东西。

但是,正如@Drewfer 所接受的答案所指出的:“如果不将信息存储在解释器的两个实例之外的某个地方,你将无法做你想做的事情”;换句话说:要么你必须使用网络/套接字设置 - 要么你必须使用临时文件(因此,“完全独立的 python 会话”没有共享 RAM)。

现在,即使考虑到这些,也很难找到可行的示例(除了)——在mmap和multiprocessingpickle的文档中也是如此。我设法找到了一些其他示例——它们也描述了文档中未提及的一些陷阱:

  • 使用mmap:在两个不同的脚本中使用 mmap 在进程间共享 Python 数据的工作代码 | schmichael 的博客

    • 演示两个脚本如何改变共享值

    • 请注意,这里创建了一个临时文件来存储已保存的数据 -mmap只是用于访问此临时文件的特殊接口

  • 使用multiprocessing:工作代码位于:

+ Python 多处理 RemoteManager 在 multiprocessing.Process 下-共享的工作示例`SyncManager`(通过) ;服务器写入,客户端读取(共享数据)`manager.start()``Queue`
+ 多处理模块和 pyro 的比较?`BaseManager` - (通过)具有共享自定义类的工作示例`server.serve_forever()`;服务器写入,客户端读取和写入
+ 如何将 python 字典与多处理同步- 这个答案对陷阱有很好的解释,并且是(通过)共享字典`multiprocessing`的一个工作示例;服务器不执行任何操作,客户端读取和写入`SyncManager``manager.start()`

感谢这些示例,我想出了一个例子,它本质上与示例相同mmap,使用“同步 Python 字典”示例中的方法 - 使用BaseManagermanager.start()通过文件路径地址)和共享列表;服务器和客户端都读取和写入(粘贴在下面)。请注意:

  • multiprocessing经理可以通过manager.start()或启动server.serve_forever()

    • serve_forever()锁定 -start()没有

    • 有自动记录功能multiprocessing:它似乎可以很好地与start()ed 进程配合使用 - 但似乎忽略了那些serve_forever()

  • 中的地址规范multiprocessing可以是 IP(套接字)或临时文件(可能是管道?)路径;在multiprocessing文档中:

+ 大多数示例使用`multiprocessing.Manager()`- 这只是一个函数(*不是*类实例),它返回一个`SyncManager`,它是的一个特殊子类`BaseManager`;并使用`start()`- 但*不*用于独立运行的脚本之间的 IPC;这里使用了文件路径
+ 还有一些其他示例`serve_forever()`适用于独立运行的脚本之间的 IPC;这里使用 IP/套接字地址
+ 如果未指定地址,则会自动使用临时文件路径(请参阅16.6.2.12. 日志记录以了解如何查看此内容的示例)

除了“同步 Python 字典”文章中提到的所有陷阱之外,在列表的情况下还存在其他陷阱。该文章指出:

对字典的所有操作都必须通过方法来完成,而不是通过字典赋值(syncdict["blast"] = 2 将会因为多处理共享自定义对象的方式而失败)

获取和设置的解决方法dict['key']是使用dict公共方法getupdate。问题是没有替代的公共方法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(对于套接字);我想这让我很高兴,因为我既不必担心网络端口,也不必担心临时文件:)

其他相关问题:

  • Python:可以在两个独立的进程之间共享内存数据(非常好的解释)

  • 高效的 Python 到 Python IPC

  • Python:将变量发送到另一个脚本

解决方案 2:

如果不将信息存储在解释器的两个实例之外的某个地方,您将无法执行所需的操作。

如果您只需要简单的变量,则可以轻松地将 Python 字典转储到脚本一中的 pickle 模块文件中,然后在脚本二中重新加载它。示例:

一.py

import pickle

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

二.py

import pickle

fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]

解决方案 3:

sudo apt-get install memcached python-memcache

一.py

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

二.py

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

解决方案 4:

您在此处尝试执行的操作(通过单独的 Python 解释器将共享状态存储在 Python 模块中)将不起作用。

模块中的值可以由一个模块更新,然后由另一个模块读取,但这必须在同一个 Python 解释器中。您在这里似乎正在做的实际上是一种进程间通信;这可以通过两个进程之间的套接字通信来实现,但它比您期望在这里完成的工作要简单得多。

解决方案 5:

您可以使用相对简单的 mmap 文件。您可以使用 shared.py 来存储公共常量。以下代码将跨不同的 python 解释器 \ 脚本 \ 进程运行

共享.py:

MMAP_SIZE = 16*1024 
MMAP_NAME = 'Global\\SHARED_MMAP_NAME'
  • “Global” 是 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
')                                                                 
    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('
')                                     
    map_file.close()                                                            
    print data                                                                  

*此代码是为 Windows 编写的,Linux 可能需要稍作调整

更多信息请访问 - https://docs.python.org/2/library/mmap.html

解决方案 6:

通过以下方式共享动态变量Redis

脚本一

from redis import Redis
from time import sleep

cli = Redis('localhost')
shared_var = 1

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

在终端中运行script_one (一个进程):

$ python script_one.py

脚本_two.py

from redis import Redis
from time import sleep

cli = Redis('localhost')

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

在另一个终端(另一个进程)中运行script_two :

$ python script_two.py

出去:

1
2
3
4
5
...

依赖项:

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

解决方案 7:

我建议你使用多处理模块。虽然你不能从命令行运行两个脚本,但你可以轻松地让两个独立的进程相互通信。

从文档的例子:

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()

解决方案 8:

您需要将变量存储在某种持久文件中。有多个模块可以执行此操作,具体取决于您的确切需求。

pickle 和 cPickle 模块可以将大多数 python 对象保存和加载到文件中。

shelve 模块可以将 python 对象存储在类似字典的结构中(在后台使用 pickle)。

dbm/bsddb/dbhash/gdm 模块可以将字符串变量存储在类似字典的结构中。

sqlite3模块可以将数据存储在轻量级SQL数据库中。

其中大多数的最大问题是它们无法在不同的进程之间同步 - 如果一个进程读取一个值而另一个进程正在写入数据存储,那么您可能会得到不正确的数据或数据损坏。要解决这个问题,您需要编写自己的文件锁定机制或使用功能齐全的数据库。

解决方案 9:

如果您想读取和修改两个单独运行的脚本之间的共享数据,一个好的解决方案是利用 Python 多处理模块并使用Pipe() 或 Queue() (请参阅此处的区别)。这样您就可以同步脚本并避免并发和全局变量问题(例如,如果两个脚本都想同时修改一个变量会发生什么)。

使用管道/队列的最好的部分是你可以通过它们传递 python 对象。

如果尚未传递数据,还有一些方法可以避免等待数据(queue.empty()和pipeConn.poll())。

请参阅下面使用 Queue() 的示例:

    # main.py
    from multiprocessing import Process, Queue
    from stage1 import Stage1
    from stage2 import Stage2


    s1= Stage1()
    s2= Stage2()

    # S1 to S2 communication
    queueS1 = Queue()  # s1.stage1() writes to queueS1

    # S2 to S1 communication
    queueS2 = Queue()  # s2.stage2() writes to queueS2

    # start s2 as another process
    s2 = Process(target=s2.stage2, args=(queueS1, queueS2))
    s2.daemon = True
    s2.start()     # Launch the stage2 process

    s1.stage1(queueS1, queueS2) # start sending stuff from s1 to s2 
    s2.join() # wait till s2 daemon finishes
    # stage1.py
    import time
    import random

    class Stage1:

      def stage1(self, queueS1, queueS2):
        print("stage1")
        lala = []
        lis = [1, 2, 3, 4, 5]
        for i in range(len(lis)):
          # to avoid unnecessary waiting
          if not queueS2.empty():
            msg = queueS2.get()    # get msg from s2
            print("! ! ! stage1 RECEIVED from s2:", msg)
            lala = [6, 7, 8] # now that a msg was received, further msgs will be different
          time.sleep(1) # work
          random.shuffle(lis)
          queueS1.put(lis + lala)             
        queueS1.put('s1 is DONE')
    # stage2.py
    import time

    class Stage2:

      def stage2(self, queueS1, queueS2):
        print("stage2")
        while True:
            msg = queueS1.get()    # wait till there is a msg from s1
            print("- - - stage2 RECEIVED from s1:", msg)
            if msg == 's1 is DONE ':
                break # ends loop
            time.sleep(1) # work
            queueS2.put("update lists")             

编辑:刚刚发现您可以使用queue.get(False)来避免接收数据时堵塞。这样就不需要先检查队列是否为空。如果您使用管道,这是不可能的。

解决方案 10:

使用文本文件或环境变量。由于两者分开运行,因此您无法真正完成您想要做的事情。

解决方案 11:

在您的示例中,第一个脚本运行完成,然后第二个脚本运行。这意味着您需要某种持久状态。其他答案建议使用文本文件或 Python 的pickle模块。就我个人而言,我很懒,如果我可以使用,我不会使用文本文件pickle;为什么我要编写解析器来解析我自己的文本文件格式?

您也pickle可以使用json模块将其存储为 JSON。如果您想将数据共享给非 Python 程序,这可能是更好的选择,因为 JSON 是一个简单而通用的标准。如果您的 Python 没有json,请获取simplejson。

如果您的需求超出了预期pickle,或者json说您实际上希望同时执行两个 Python 程序并实时更新持久状态变量,我建议您使用SQLite数据库。使用 ORM 抽象数据库,这非常简单。对于 SQLite 和 Python,我推荐使用Autumn ORM。

解决方案 12:

这种方法对我来说似乎很简单:

类共享类:

def __init__(self):
    self.data = {}

def set_data(self, name, value):
    self.data[name] = value

def get_data(self, name):
    try:
        return self.data[name]
    except:
        return "none"

def reset_data(self):
    self.data = {}

共享类 = 共享类()

PS:您可以使用参数名称和值来设置数据,并且可以使用 get_data 方法访问该值,以下是示例:

设置数据

示例 1:

sharedClass.set_data("name","Jon Snow")

示例 2:

sharedClass.set_data("email"," jon@got.com ")\

获取数据

sharedClass.get_data(“电子邮件”)\

要重置整个状态,只需使用

共享类.重置数据()

它是一种从 json 对象(在本例中为 dict)访问数据的方法

希望这有帮助...

解决方案 13:

您可以使用python中的基本fromimport函数将变量导入two.py。例如:

from filename import variable

这样就可以从文件中导入变量了。(当然,你应该filename用替换one.py,并variable用你想要共享到 的变量替换two.py。)

解决方案 14:

您还可以通过将变量设为全局变量来解决此问题

python first.py

class Temp:
    def __init__(self):
        self.first = None

global var1
var1 = Temp()
var1.first = 1
print(var1.first)

python 第二.py

import first as One
print(One.var1.first)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用