【问题标题】:How to call a method from an already running python script如何从已经运行的 python 脚本中调用方法
【发布时间】:2014-04-21 08:25:30
【问题描述】:

我需要检查 python 脚本是否已经在运行,然后从同一个正在运行的 python 脚本中调用一个方法。但它必须在同一个进程(pid)上,没有新进程。这可能吗?

我尝试了一些代码但没有成功。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter as tk
from Tkinter import *
import socket


class Main():
    def mainFunc(self):
        self.root = tk.Tk()
        self.root.title("Main Window")

        self.lbl = Label(self.root, text = "First Text")
        self.lbl.pack()

        openStngs = Button(self.root, text = "Open Settings", command=self.settingsFunc)
        openStngs.pack()

    def settingsFunc(self):
        stngsRoot = Toplevel()
        stngsRoot.title("Settings Window")

        changeTextOfLabel = Button(stngsRoot, text = "Change Main Window Text", command=self.change_text)
        changeTextOfLabel.pack()

    def change_text(self):
        self.lbl.config(text="Text changed")

# the get_lock from http://stackoverflow.com/a/7758075/3254912
def get_lock(process_name):
    lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
    try:
        print lock_socket
        lock_socket.bind('\0' + process_name)
        print 'I got the lock'

        m.mainFunc()
        mainloop()

    except socket.error:
        print 'lock exists'
        m.settingsFunc()
        mainloop()
        # sys.exit()

if __name__ == '__main__':
    m=Main()
    get_lock('myPython.py')

【问题讨论】:

  • 我认为这是不可能的。 Python 不是像 C/C++ 这样的编译语言,你可以从它的内存地址调用一个函数(如果你不在同一个内存空间,这也会非常复杂)。我认为您可以使用其他方法实现您想做的任何事情。

标签: python methods call


【解决方案1】:

你要么需要:

  • 主动检查正在运行的进程以查看环境(例如,文件的内容或通过套接字传入的数据)以了解何时触发该函数,
  • 或者让您正在运行的进程接收 unix 信号或其他一些 IPC(可能是用户定义的信号之一)并在收到信号时执行一项功能。

无论哪种方式,您都不能只进入一个正在运行的进程并在该进程中触发一个函数(如果您将正在运行的进程连接到调试器,这可能不是字面上不可能的,但我不建议这样做)。

Tkinter 必然有自己的事件循环系统,所以我建议阅读它是如何工作的,以及如何在该事件循环系统中的计时器上运行某些东西,或者设置一个响应信号的回调。您还可以将基于非事件循环的系统包装在 try/except 块中,该块将捕获由 UNIX 信号生成的异常,但在捕获该信号后恢复程序其余部分的操作可能并不简单,在那种情况下。

【讨论】:

  • 谢谢,我是 python 新手,需要更多努力。
  • 很好的整体答案。
【解决方案2】:

套接字是解决这种进程间通信问题的好方法。

一种可能的方法是在原始进程的线程中设置套接字服务器,这可以用作外部输入的入口点。一个(相当愚蠢的)例子可能是:

# main.py

import socket
import SocketServer # socketserver in Python 3+
import time
from Queue import Queue
from threading import Thread

# class for handling requests
class QueueHandler(SocketServer.BaseRequestHandler):
    def __init__(self, request, client_address, server):
        self.server = server
        server.client_address = client_address
        SocketServer.BaseRequestHandler.__init__(self,request, client_address, server)

    # receive a block of data
    # put it in a Queue instance
    # send back the block of data (redundant)  
    def handle(self):
        data = self.request.recv(4096)
        self.server.recv_q.put(data)
        self.request.send(data)


class TCPServer(SocketServer.TCPServer):
    def __init__(self, ip, port, handler_class=QueueHandler):
        SocketServer.TCPServer.__init__(self, (ip, port), handler_class, bind_and_activate=False)
        self.recv_q = Queue() # a Queue for data received over the socket
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_bind()
        self.server_activate()

    def shutdown(self):
        SocketServer.TCPServer.shutdown(self)

    def __del__(self):
        self.server_close()


# This is the equivalent of the main body of your original code
class TheClassThatLovesToAdd(object):
    def __init__(self):
        self.value = 1

        # create and instance of the server attached to some port
        self.server = TCPServer("localhost",9999)

        # start it listening in a separate control thread
        self.server_thread = Thread(target=self.server.serve_forever)
        self.server_thread.start()
        self.stop = False

    def add_one_to_value(self):
        self.value += 1

    def run(self):
        while not self.stop:
            print "Value =",self.value

            # if there is stuff in the queue...
            while not self.server.recv_q.empty():

                # read and parse the message from the queue
                msg = self.server.recv_q.get()

                # perform some action based on the message
                if msg == "add":
                    self.add_one_to_value()
                elif msg == "shutdown":
                    self.server.shutdown()
                    self.stop = True

            time.sleep(1)

if __name__ == "__main__":
    x = TheClassThatLovesToAdd()
    x.run()

当您开始运行时,它应该只是循环打印到屏幕上。输出:

Value = 1
Value = 1
Value = 1
...

但是,附加到TheClassThatLovesToAdd 实例的TCPServer 实例现在为我们提供了控制路径。最简单的控制代码 sn-p 是:

# control.py

import socket
import sys

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.settimeout(2)
sock.connect(('localhost',9999))

# send some command line argument through the socket
sock.send(sys.argv[1])
sock.close()

所以如果我在一个终端窗口中运行main.py 并从另一个窗口调用python control.py addmain.py 的输出将会改变:

Value = 1
Value = 1
Value = 1
Value = 2
Value = 2
...

最后我们可以运行 python control.py shutdown 来杀死它,它会轻轻地让 main.py 停止。

这绝不是您问题的唯一解决方案,但它可能是最简单的解决方案之一。

【讨论】:

  • 谢谢,但我认为这对我的小 python 脚本来说将是非常大的努力。 :)
  • 不用担心。您要求做的事情非常复杂,因此在几行代码中实现它不会那么容易。
【解决方案3】:

可以尝试 GDB,但不确定如何从 [空闲线程] 中调用函数。

也许精通 gdb 和从 GDB 中调试/调用 Python 函数的人可以改进这个答案。

【讨论】:

    【解决方案4】:

    一种解决方案是使用消息传递服务(例如 ActiveMQ 或 RabbitMQ)。您的应用程序订阅了一个队列/主题,并且每当您想向它发送命令时,您都会向它的队列写入一条消息。我不打算详细介绍,因为网上有成千上万的例子。队列/消息/MQTT 等非常易于实现,并且是大多数业务系统(和现代控制系统)的通信方式。搜索 paho-mqtt。

    【讨论】:

      猜你喜欢
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      • 2016-07-21
      • 2019-12-31
      • 1970-01-01
      • 1970-01-01
      • 2015-09-05
      • 1970-01-01
      相关资源
      最近更新 更多