Python 析构函数中的分段错误
Segmentation fault in destructor with Python
我制作了一个 class 来代表我的 LED 灯带,我想在我停止它时关闭灯带(也就是当程序停止并且对象被销毁时)。因此,正如我在 C++ 中所做的那样,我创建了一个析构函数来执行此操作。但看起来 Python 在 它销毁了对象之后称它为 。然后我得到了一个分段错误。
这是我的 class,析构函数只需调用函数将每个 LED 的颜色设置为 0。
class LedStrip:
def __init__(self, led_count, led_pin, led_freq_hz, led_dma, led_invert, led_brightness, led_channel, color = MyColor(0,0,0)):
self.__strip = Adafruit_NeoPixel(led_count, led_pin, led_freq_hz, led_dma, led_invert, led_brightness, led_channel)
self.__color = color
self.__strip.begin()
def __del__(self):
self.__color = MyColor(0,0,0)
self.colorWipe(10)
# ATTRIBUTS (getter/setter)
@property
def color(self):
return self.__color
@color.setter
def color(self, color):
if isinstance(color, MyColor):
self.__color = color
else:
self.__color = MyColor(0,0,0)
def __len__(self):
return self.__strip.numPixels()
# METHODS
def colorWipe(self, wait_ms=50):
"""Wipe color across display a pixel at a time."""
color = self.__color.toNum()
for i in range(self.__strip.numPixels()):
self.__strip.setPixelColor(i, color)
self.__strip.show()
time.sleep(wait_ms/1000.0)
MyColor
只是我用来表示 RGB 颜色的 class。在 Python 中完成该任务的正确方法是什么?我来自 C++,因此我的 OOP 方法实际上是面向 C++ 的,所以我在用 pythonic 方式思考时遇到了一些困难。
提前致谢
这么说吧。首先,“...就像我在 C++ 中所做的那样”方法是不合适的,我相信您自己也了解。不用说 Python 是完全不同的语言。但在这种特殊情况下,应该强调一下,因为 Python 的内存管理与 C++ 有很大不同。 Python 使用引用计数,当对象引用计数变为零时,它的内存将被释放(即当一个对象被垃圾收集 ) 等等。
Python 用户自定义对象有时确实需要定义__del__()
方法。但它在任何意义上都不是 析构函数 (肯定不是 C++ 意义上的),它是 finalizer。此外,不保证 __del__()
方法是为解释器退出时仍然存在的对象调用的。然而我们可以显式调用 __del__()
,这不适合你的情况,至于这个我建议将 LED 关闭作为一种显式方法,而不依赖于 Python 的内部结构。就像 Zen of Python (import this
command).
Explicit is better than implicit.
有关 __del__()
的更多信息,请查看此 good answer. For more on reference counting check this article。
您在编写 __del__
方法(终结器)时必须 非常 小心。在一个对象不再被引用后几乎任何时候都可以调用它们(它不一定立即发生)并且实际上不能保证它们会在解释器退出时被调用。如果它们确实在解释器退出期间被调用,则其他对象(例如全局变量和其他模块)可能已经被清理,因此对您的终结器不可用。它们的存在是为了让对象可以清理状态(例如低级文件句柄、连接等),并且不像 C++ 析构函数那样运行。根据我的 Python 经验,您很少需要编写自己的 __del__
方法。
您可以在此处使用其他机制。一种选择是 try/finally
:
leds = LedStrip(...)
try:
# application logic to interact with the LEDs
finally:
leds.clear() # or whatever logic you need to clear the LEDs to zero
这还是很明确的。如果你想要更隐含的东西,你可以考虑使用 Python context manager 结构。要使用上下文管理器,您可以使用 with
关键字:
with open("file.txt", "w") as outfile:
outfile.write("Hello!\n")
with
语句调用特殊的__enter__
方法来初始化“上下文”。当块结束时,将调用 __exit__
方法来结束“上下文”。对于文件,__exit__
将关闭文件。关键是 __exit__
即使块内发生异常也会被调用(有点像 try
块上的 finally
)。
您可以在 LED 灯条上实现 __enter__
和 __exit__
,然后写:
with LedStrip(...) as leds:
# do whatever you want with the leds
当块结束时,__exit__
方法可以重置所有 LED 的状态。
我制作了一个 class 来代表我的 LED 灯带,我想在我停止它时关闭灯带(也就是当程序停止并且对象被销毁时)。因此,正如我在 C++ 中所做的那样,我创建了一个析构函数来执行此操作。但看起来 Python 在 它销毁了对象之后称它为 。然后我得到了一个分段错误。
这是我的 class,析构函数只需调用函数将每个 LED 的颜色设置为 0。
class LedStrip:
def __init__(self, led_count, led_pin, led_freq_hz, led_dma, led_invert, led_brightness, led_channel, color = MyColor(0,0,0)):
self.__strip = Adafruit_NeoPixel(led_count, led_pin, led_freq_hz, led_dma, led_invert, led_brightness, led_channel)
self.__color = color
self.__strip.begin()
def __del__(self):
self.__color = MyColor(0,0,0)
self.colorWipe(10)
# ATTRIBUTS (getter/setter)
@property
def color(self):
return self.__color
@color.setter
def color(self, color):
if isinstance(color, MyColor):
self.__color = color
else:
self.__color = MyColor(0,0,0)
def __len__(self):
return self.__strip.numPixels()
# METHODS
def colorWipe(self, wait_ms=50):
"""Wipe color across display a pixel at a time."""
color = self.__color.toNum()
for i in range(self.__strip.numPixels()):
self.__strip.setPixelColor(i, color)
self.__strip.show()
time.sleep(wait_ms/1000.0)
MyColor
只是我用来表示 RGB 颜色的 class。在 Python 中完成该任务的正确方法是什么?我来自 C++,因此我的 OOP 方法实际上是面向 C++ 的,所以我在用 pythonic 方式思考时遇到了一些困难。
提前致谢
这么说吧。首先,“...就像我在 C++ 中所做的那样”方法是不合适的,我相信您自己也了解。不用说 Python 是完全不同的语言。但在这种特殊情况下,应该强调一下,因为 Python 的内存管理与 C++ 有很大不同。 Python 使用引用计数,当对象引用计数变为零时,它的内存将被释放(即当一个对象被垃圾收集 ) 等等。
Python 用户自定义对象有时确实需要定义__del__()
方法。但它在任何意义上都不是 析构函数 (肯定不是 C++ 意义上的),它是 finalizer。此外,不保证 __del__()
方法是为解释器退出时仍然存在的对象调用的。然而我们可以显式调用 __del__()
,这不适合你的情况,至于这个我建议将 LED 关闭作为一种显式方法,而不依赖于 Python 的内部结构。就像 Zen of Python (import this
command).
Explicit is better than implicit.
有关 __del__()
的更多信息,请查看此 good answer. For more on reference counting check this article。
您在编写 __del__
方法(终结器)时必须 非常 小心。在一个对象不再被引用后几乎任何时候都可以调用它们(它不一定立即发生)并且实际上不能保证它们会在解释器退出时被调用。如果它们确实在解释器退出期间被调用,则其他对象(例如全局变量和其他模块)可能已经被清理,因此对您的终结器不可用。它们的存在是为了让对象可以清理状态(例如低级文件句柄、连接等),并且不像 C++ 析构函数那样运行。根据我的 Python 经验,您很少需要编写自己的 __del__
方法。
您可以在此处使用其他机制。一种选择是 try/finally
:
leds = LedStrip(...)
try:
# application logic to interact with the LEDs
finally:
leds.clear() # or whatever logic you need to clear the LEDs to zero
这还是很明确的。如果你想要更隐含的东西,你可以考虑使用 Python context manager 结构。要使用上下文管理器,您可以使用 with
关键字:
with open("file.txt", "w") as outfile:
outfile.write("Hello!\n")
with
语句调用特殊的__enter__
方法来初始化“上下文”。当块结束时,将调用 __exit__
方法来结束“上下文”。对于文件,__exit__
将关闭文件。关键是 __exit__
即使块内发生异常也会被调用(有点像 try
块上的 finally
)。
您可以在 LED 灯条上实现 __enter__
和 __exit__
,然后写:
with LedStrip(...) as leds:
# do whatever you want with the leds
当块结束时,__exit__
方法可以重置所有 LED 的状态。