【问题标题】:Accessing python class variable defined inside the main module of a script访问脚本主模块中定义的python类变量
【发布时间】:2014-10-25 19:19:04
【问题描述】:

我有一个使用 celery 进行异步任务处理的 django 项目。我正在使用 python 2.7。

我的 django 项目中的模块 client.py 中有一个类:

# client.py
class Client:
    def __init__(self):
        # code for opening a persistent connection and saving the connection client in a class variable
        ...
        self.client = <connection client>

    def get_connection_client(self):
        return self.client

    def send_message(self, message):
        # --- Not the exact code but this is the function I need to access to for which I need access to the client variable---
        self.client.send(message)
    # Other functions that use the above method to send messages
    ...

该类只需实例化一次即可创建到远程服务器的持久连接

我运行了一个无限期运行的脚本connection.py

# connection.py
from client import Client
if __name__ == '__main__':
    clientobj = Client()
    client = clientobj.get_connection_client()
    # Blocking process
    while True:
        # waits for a message from the remote server
        ...

我需要从另一个模块 tasks.py 访问变量 client(需要 celery)。


# tasks.py
...
from client import Client
@app.task
def function():
    # Need access to the client variable
    # <??? How do I get an access to the client variable for the 
    # already established connection???>
    message = "Message to send to the server using the established connection"
    client.send_message(message)

所有三个 python 模块都在同一台机器上。 connection.py 作为独立脚本执行并首先执行。 tasks.py 中的方法 function() 在需要时在项目的其他模块中多次调用,因此,我无法在此方法中实例化 Client 类。全局变量不起作用。


在java中,我们可以创建全局静态变量并在整个项目中访问它。我们如何在 python 中做到这一点?

我能想到但不确定是否可以在python中完成的方法:

  • 将此变量保存在一个公用文件中,以便在我的项目中的其他模块中可以访问它?
  • 将此客户端保存为 django 或 celery 中的设置并在所需模块中访问此设置?
  • 根据 sebastian 的建议,另一种方法是在正在运行的进程之间共享变量。我本质上想这样做。我如何在 python 中做到这一点?

如果有兴趣了解为什么需要这样做,请see this question。它解释了完整的系统设计和涉及的各种组件。

我也愿意接受需要更改代码结构的建议。

【问题讨论】:

    标签: python django python-2.7 celery


    【解决方案1】:

    multiprocessing 提供了执行此操作所需的所有工具。

    connection.py

    from multiprocessing.managers import BaseManager
    from client import Client()
    client = Client()
    class ClientManager(BaseManager): pass
    ClientManager.register('get_client', callable=lambda: client)
    manager = ClientManager(address=('', 50000), authkey='abracadabra')
    server = manager.get_server()
    server.serve_forever()
    

    tasks.py

    from multiprocessing.managers import BaseManager
    class ClientManager(BaseManager): pass
    ClientManager.register('get_client')
    manager = ClientManager(address=('localhost', 50000), authkey='abracadabra')
    manager.connect()
    client = manager.get_client()
    
    @app.task
    def function():
        message = "Message to send to the server using the established connection"
        client.send_message(message)
    

    【讨论】:

    • 附带说明,该函数将只能接受可以腌制的参数。这适用于大多数类型(当然也适用于所有内置类型)。
    【解决方案2】:

    我没有使用 django 的经验,但如果它们是从同一个脚本执行的,你可以使客户端成为单例,或者可以在 init.py 中声明客户端,然后将其导入到任何地方你需要它。

    如果你选择单例,你可以为此做一个装饰器:

    def singleton(cls):
        instances = {}
    
        def get_instance(*args, **kwargs):
            if cls not in instances:
                instances[cls] = cls(*args, **kwargs)
            return instances[cls]
    
        return get_instance
    

    然后你会定义:

    # client.py
    
    @singleton
    class Client:
        def __init__(self):
            # code for opening a persistent connection and saving the connection client in a class variable
            ...
            self.client = <connection client>
    
        def get_connection_client(self):
            return self.client
    

    这就是我所能提供的所有建议。也许试着更好地解释一下一切是如何运行的或所涉及的部分。

    【讨论】:

    • 用有关客户端使用情况和一切执行方式的信息更新了问题。
    • 连接脚本原样,完成后将杀死并释放客户端变量......因此,它们是单独的脚本,因此是单独的进程。假设你做了一些事情来保持客户端的活力,你需要以某种方式远程使用它(在任何其他运行的脚本中),所以我认为你需要在进程之间进行通信并共享这些变量。我不确定你为什么需要这样做,但这需要一些严肃的逻辑来在进程之间进行通信。
    • 更新问题。请参阅编辑 1 和 2。我的系统设计在这个问题中进行了解释 - stackoverflow.com/questions/25442240/…
    • 是的,我正在寻找一种在两个正在运行的进程之间共享变量的方法。我们如何做到这一点?
    • 不得不删除“编辑”标签。请查看代码 sn-ps 中的更新。
    【解决方案3】:

    Python 具有类属性(实例之间共享的属性)和类方法(作用于类本身的方法)。两者在类和实例上都是可读的。

    # client.py
    class Client(object):
        _client = None
    
        @classmethod
        def connect(cls):
            # dont do anything if already connected
            if cls._client is None:
                return
    
            # code for opening a persistent connection and saving the connection client in a class variable
            ...
            cls._client = <connection client>
    
    
        @classmethod
        def get_connection_client(cls):
            return cls._client
    
    
        def __init__(self):
            # make sure we try to have a connection on initialisation
            self.connect()
    

    现在我不确定这是解决您问题的最佳方法。

    【讨论】:

    • 谢谢。但我需要从不同的模块访问定义的client 变量。我无法使用类方法来做到这一点。请查看问题中所做的修改。
    【解决方案4】:

    如果 connection.py 正在导入 tasks.py,您可以在您的 tasks.py 中进行:

    import __main__ # connection.py
    main_globals = __main__.__dict__ # this "is" what you getting in connection.py when you write globals()
    client = main_globals["client"]  # this client has the same id with client in connection.py
    

    BaseManager 也是一个答案,但它在 localhost 上使用套接字网络,如果您尚未使用多处理,它不是访问变量的好方法。我的意思是如果你需要使用多处理,你应该使用 BaseManager。但是如果你不需要多处理,那么使用多处理不是一个好的选择。我的代码只是从 connection.py 中获取“客户端”变量的指针 口译员。

    另外如果你想使用多处理,我的代码将无法工作,因为不同进程中的解释器是不同的。

    【讨论】:

      【解决方案5】:

      从文件中读取时使用pickle

      【讨论】:

        猜你喜欢
        • 2019-01-03
        • 2016-09-13
        • 2015-05-15
        • 1970-01-01
        • 1970-01-01
        • 2022-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多