首页 > 解决方案 > 如何正确完成/清理 pythonnet CLR 对象?

问题描述

我正在使用 python 通过提供的 API 与某个软件进行通信。由于 API 代码是用 C# 编写的,我使用 pythonnet 导入 DLL 并随后使用它。在优化代码时使用例如 Jupyter Lab 或 Jupyter Notebook 这样做是有益的,因为您可以轻松地比较代码和软件中的结果。但是,我遇到了清理问题。API 要求您通过运行以下代码来建立连接

import clr
clr.AddReference('API')
api = __import__('API', globals(), locals(), [], 0)
connection = api.connection()
app = connection.connect()

现在您可以使用app. 我的问题的主要原因是您只能在 CLR 中拥有一个应用程序。如果你想创建一个新的,你应该调用app.close()和 随后newapp = connection.connect()。没有明确定义当您创建newapp而不调用时会发生什么。app.close()我不确定 C# 将如何处理这个问题,它会覆盖app内存中的内存,app现在还会指向newapp,还是其他什么?有了这个,我更加不确定 python+CLR 是如何处理它的。

为了确保您始终使用正确连接的应用程序,我创建了一个只允许app存在一个实例的类。此限制是通过 API 评估来实现connection.Alive的,当应用程序已生成但尚未正确关闭时,该 API 为 True。该类类似于:

class APIWrapper:
    def __init__(self, api):
        self.API = api
        self.Connection = api.connection()
   
    def connect():
        if self.Connection.Alive:
            raise RunTimeError('Only one live application is allowed at runtime')

        app = self.Connection.connect()
        return app

虽然这很好用,但当我不小心做了类似的事情时,我的问题就出现了:

wrap = APIWrapper()
wrap.connect()

这样做时,应用程序上线并且 wrap.Connection.Alive 评估为 True。但是,由于我没有将返回值分配给wrap.connect()变量,因此无法使用app.close(). 例如,如果我这样做:

wrap = APIWrapper()
print(wrap.Connection.Alive)  # -> False
app = wrap.connect()
print(wrap.Connection.Alive)  # -> True
app.close()
print(wrap.Connection.Alive)  # -> False
wrap.connect()
print(wrap.Connection.Alive)  # -> True

我不能再关闭连接了。我考虑过改变类,以便只绑定wrap.connect()wrap.App并允许通过属性进行访问。这将解决丢失应用程序的问题,但我宁愿不必为了代码可读性而连续调用 wrap.App。另外,我只是想知道是否有适当的方法来处理这些最终确定问题?

标签: pythonc#apicode-cleanuppython.net

解决方案


首先,如果调用wrap.connect()时没有在任何地方存储返回值是问题所在,那么有一个简单的解决方案:不要这样做!看起来连接是一种资源,因此您必须跟踪它以在时机成熟时正确释放它。

在您的示例中,当有人connect()再次调用时,先前创建的连接会发生什么?

其次,在 Python 中有两种方法可以显式地跟踪资源:

  1. with语句 + 上下文管理器(强烈推荐)。在这种情况下,您需要在包装器上实现上下文管理器。
  2. __del__您可以定义的函数,它将在不再需要对象时调用。这个你应该避免,因为它会在任意时间执行,这意味着当你尝试创建一个新的连接时,旧的连接可能仍然存在,因为 Python 还没有意识到它应该调用它__del__

另一种选择是制作一个单例


推荐阅读