Python 包在调用 __del__ 之前卸载
Python package unloaded before __del__ is called
我正在使用 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
class 的对象“重要”,因此在所有对象都被销毁之前它不会被卸载?
一般来说,您不能假定 __del__
会被调用。如果您来自 C++ 等 RAII(资源分配即初始化)语言,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.
我认为 通常被认为是推荐的解决方案,但上下文管理器并不总是合适,具体取决于应用程序的结构。
另一种选择是在应用程序退出时显式调用清理函数。您可以通过将整个应用程序包装在 try/finally 中并使用 finally 子句进行清理来使其更安全。请注意,如果您不包含 catch,则在执行 finally 后将自动重新引发异常,这可能正是您想要的。示例:
app = Application()
try:
app.run()
finally:
app.cleanup()
不过请注意,您可能只是抛出了一个异常。如果异常发生,例如,在通信中,那么您可能无法发送命令来重置输出,因为设备可能希望您完成已经开始的工作。
我在使用另一种连接设备时遇到了同样的安全问题。我无法安全地预测 __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)
最后,我使用包 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) #
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
class并不重要,最后高压将被关闭。
我正在使用 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
class 的对象“重要”,因此在所有对象都被销毁之前它不会被卸载?
一般来说,您不能假定 __del__
会被调用。如果您来自 C++ 等 RAII(资源分配即初始化)语言,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.
我认为
另一种选择是在应用程序退出时显式调用清理函数。您可以通过将整个应用程序包装在 try/finally 中并使用 finally 子句进行清理来使其更安全。请注意,如果您不包含 catch,则在执行 finally 后将自动重新引发异常,这可能正是您想要的。示例:
app = Application()
try:
app.run()
finally:
app.cleanup()
不过请注意,您可能只是抛出了一个异常。如果异常发生,例如,在通信中,那么您可能无法发送命令来重置输出,因为设备可能希望您完成已经开始的工作。
我在使用另一种连接设备时遇到了同样的安全问题。我无法安全地预测 __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)
最后,我使用包 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) #
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
class并不重要,最后高压将被关闭。