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 的状态。