【问题标题】:Python package unloaded before __del__ is called在调用 __del__ 之前卸载的 Python 包
【发布时间】:2020-12-04 14:34:36
【问题描述】:

我正在使用pyvisa 通过 USB 与仪器进行通信。我能够正确控制它。由于它是高压源,并且打开高压时忘记它很危险,我想实现__del__方法以便在代码执行完成时关闭输出。所以基本上我写了这个:

import pyvisa as visa

class Instrument:
    def __init__(self, resource_str='USB0::1510::9328::04481179::0::INSTR'):
        self._resource_str = resource_str
        self._resource = visa.ResourceManager().open_resource(resource_str)
    
    def set_voltage(self, volts: float):
        self._resource.write(f':SOURCE:VOLT:LEV {volts}')
    
    def __del__(self):
        self.set_voltage(0)

instrument = Instrument()
instrument.set_voltage(555)

问题是它不工作并且在我得到的终端中

$ python3 comunication\ test.py 
Exception ignored in: <function Instrument.__del__ at 0x7f4cca419820>
Traceback (most recent call last):
  File "comunication test.py", line 12, in __del__
  File "comunication test.py", line 9, in set_voltage
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py", line 197, in write
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py", line 157, in write_raw
  File "/home/superman/.local/lib/python3.8/site-packages/pyvisa/resources/resource.py", line 190, in session
pyvisa.errors.InvalidSession: Invalid session handle. The resource might be closed.

我猜发生的事情是在我的对象的__del__ 方法被调用之前,pyvisa 被“删除”了。我怎样才能防止这种情况?我如何告诉 Python pyvisa 对于 Instrument 类的对象是“重要的”,因此在所有对象都被销毁之前它不会被卸载?

【问题讨论】:

    标签: python del pyvisa


    【解决方案1】:

    一般来说,你不能假设__del__ 会被调用。如果您来自 RAII(资源分配是初始化)语言,例如 C++,Python 不会对析构函数做出类似的保证。

    为确保某些操作被逆转,您应该考虑替代方案,例如上下文管理器:

    from contextlib import contextmanager
    
    @contextmanager
    def instrument(resource_str='USB0::1510::9328::04481179::0::INSTR'):
        ... 
        try:
            ... # yield something
        finally:
            # set voltage of resource to 0 here
    

    你会像这样使用它

    with instrument(<something>) as inst:
       ... 
    # guaranteed by here to set to 0.
    

    【讨论】:

      【解决方案2】:

      最后我使用包atexit找到了我的答案here。这正是我想做的(基于我到目前为止的测试):

      import pyvisa as visa
      import atexit
      
      class Instrument:
          def __init__(self, resource_str):
              self._resource = visa.ResourceManager().open_resource(resource_str)
              
              # Configure a safe shut down for when the class instance is destroyed:
              def _atexit():
                  self.set_voltage(0)
              atexit.register(_atexit) # https://stackoverflow.com/a/41627098
          
          def set_voltage(self, volts: float):
              self._resource.write(f':SOURCE:VOLT:LEV {volts}')
          
      instrument = Instrument(resource_str = 'USB0::1510::9328::04481179::0::INSTR')
      instrument.set_voltage(555)
      

      这种方案的优点是用户独立,不管用户如何实例化Instrument类,最终高压都会被关闭。

      【讨论】:

        【解决方案3】:

        我相信Ami Tavory's answer 通常被认为是推荐的解决方案,尽管上下文管理器并不总是适合,具体取决于应用程序的结构。

        另一种选择是在应用程序退出时显式调用清理函数。您可以通过将整个应用程序包装在 try/finally 中来使其更安全,并使用 finally 子句进行清理。请注意,如果您不包含 catch,则在执行 finally 后异常将自动重新引发,这可能是您想要的。示例:

        app = Application()
        try:
            app.run()
        finally:
            app.cleanup()
        

        但请注意,您可能只是抛出了一个异常。如果发生异常,例如在通信中途,那么您可能无法发送重置输出的命令,因为设备可能希望您完成已经开始的工作。

        【讨论】:

          【解决方案4】:

          我在使用另一种类型的连接设备时遇到了同样的安全问题。我无法安全地预测 __del__ 方法的行为,如问题中所述 I don't understand this python __del__ behaviour。 我以上下文管理器结束。在您的情况下看起来像这样:

              def __enter__(self):
                  """
                  Nothing to do.
                  """
                  return self
          
              def __exit__(self, type, value, traceback):
                  """
                  Set back to zero voltage.
                  """
                  self.set_voltage(0)
          
          with Instrument() as instrument:
              instrument.set_voltage(555)
          

          【讨论】:

          • __enter__ 方法中,您忘记返回self
          • 确实不见了。
          猜你喜欢
          • 2017-09-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-05
          • 1970-01-01
          • 2017-04-08
          • 2015-08-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多