在应用程序结束时不会调用__del__的情况

时间:2014-06-05 17:22:01

标签: python-2.7 garbage-collection

CumulativeSender在应用程序结束时发送电子邮件。我阅读了有关垃圾收集器和__del__的问题,我想知道是否存在__del__ staticCumulativeSenderObject在应用程序结束时不会被调用的情况以及为什么?

CumulativeSender.py:

import smtplib;

_staticCumulativeSenderObject = None;


class CumulativeSender(object):

    def __init__(self):

        recipients['sender'] = 'from@domain.com';
        recipients['emails'] = ['to@domain.com'];
        recipients['subject'] = 'Test Email';
        recipients['smtp_host'] = 'localhost';

        self.message = "";
        self.fromaddr = recipients['sender'];
        self.toaddrs  = recipients['emails'];
        self.subject = recipients['subject'];

        self.server = smtplib.SMTP(recipients['smtp_host']);

    def __del__(self):
        if(self.message):
            msg = ("From: %s\r\nTo: %s\r\n" % (self.fromaddr, ", ".join(self.toaddrs))) + "Subject: " + self.subject + "\r\n\r\n" + self.message;
            self.server.sendmail(self.fromaddr, self.toaddrs, msg);
        self.server.quit();

    def immediateSend():
        global _staticCumulativeSenderObject;
        if(_staticCumulativeSenderObject is not None):
            toDelete = _staticCumulativeSenderObject;
            _staticCumulativeSenderObject = None;
            del toDelete;


    immediateSend = staticmethod(immediateSend);


    def addEvent(msg):
        global _staticCumulativeSenderObject;
        if(_staticCumulativeSenderObject is None):
            _staticCumulativeSenderObject = CumulativeSender();
        _staticCumulativeSenderObject.message += msg + "\r\n";

    addEvent = staticmethod(addEvent);


    def addAlert(msg, code = None):
        CumulativeSender.addEvent(buildMessage("Allert",msg,code));

    addAlert = staticmethod(addAlert);


    def addError(msg, code = None):
        CumulativeSender.addEvent(buildMessage("Error",msg,code));

    addError = staticmethod(addError);

简单使用:

import CumulativeSender;
CumulativeSender.CumulativeSender.addAlert("alert1", 345);
CumulativeSender.CumulativeSender.addAlert("alert2", 346);

或许还有另一种方法可以做我想要的事情?

2 个答案:

答案 0 :(得分:2)

好吧,你永远不应该继续__del__。但你的问题是“什么时候不会调用__del__方法?”。这实际上很简单,只要你有一个指向你的对象的循环引用。在这种情况下,Python无法区分应首先删除哪个对象,因此Python根本不会调用任何__del__方法。更难的问题是,当你实际上可以获得循环引用时,但这并不能解决你的问题。

__del__方法的错误使用通常来自trying to use RAII technique

你的代码运行良好,直到有人从循环引用中涉及的对象引用_staticCumulativeSenderObject。但即使它看起来像一个私人,没有人应该做任何参考,这是一个糟糕的设计。此代码将完全符合atexit模块的用途。

import atexit

@atexit.register
def _sendAtExit():
    global _staticCumulativeSenderObject
    if _staticCumulativeSenderObject:
         #do what your __del__ is doing
        _staticCumulativeSenderObject.send_and_close()
        _staticCumulativeSenderObject=None

答案 1 :(得分:0)

你需要有一个很好的理由来覆盖__del__。在绝大多数情况下,缺点超过了优点。您可以使用您选择的网络搜索引擎找到大量关于__del__的阅读材料。

您没有解释为什么您更喜欢隐式方法,而不是添加send()方法并在您要发送电子邮件时明确调用它。那将是我最终的建议。

但是,相信您确实有充分理由采用隐式方法,您可以通过定义__enter____exit__方法(或使用{{3}来定义context manager。快捷方式)。用法如下:

with CumulativeSender(...) as sender:
    sender.addEvent(...)
    sender.add...(...)
    sender.add...(...)

退出此块时,将调用__exit__方法。你可以在那里发送电子邮件。

请注意,如果异常离开该块,也会调用__exit__,因此如果引发异常(可能不是),您应该决定是否要发送天气。

但是,contextlib.contextmanager