1. 程式人生 > >Python網路程式設計:E-mail服務(五)深入理解email模組的message和MIME類

Python網路程式設計:E-mail服務(五)深入理解email模組的message和MIME類

簡介

本文主要介紹Python標準庫email的message和MIME類,並分析了相關的實現,能夠是讀者更好的使用email模組。

核心類Message

Message類是email的核心類,它是email物件模型中基類,提供了設定和查詢郵件頭部,訪問訊息體的核心方法。從概念上講,Message物件構成了郵件頭部(Headers)和訊息體(payloads)。頭部格式在RFC 2822中進行了定義,每個頭部由該項名字和值組成,並由冒號分割。訊息體可以是簡單訊息物件的字串或多個MIME容器的Message物件組成的多部分郵件。Message類在email.message模組中定義。 Message基類與MIME類的繼承關係如下圖所示:

MIMEBase基類

MIMEBase作為MIME相關物件基類繼承了Message,擁有擁有Message操作郵件頭部和郵件體的所有函式。MIME在郵件頭部增加了Content-Type和MIME-Version兩個頭部資訊,從下面MIMEBase的實現中可以清楚的看到這一點:
class MIMEBase(message.Message):
    """Base class for MIME specializations."""

    def __init__(self, _maintype, _subtype, **_params):
        """This constructor adds a Content-Type: and a MIME-Version: header.

        The Content-Type: header is taken from the _maintype and _subtype
        arguments.  Additional parameters for this header are taken from the
        keyword arguments.
        """
        message.Message.__init__(self)
        ctype = '%s/%s' % (_maintype, _subtype)
        self.add_header('Content-Type', ctype, **_params)
        self['MIME-Version'] = '1.0'

多部分郵件類MIMEMultipart類

MIMEMultipart類用於實現多部分郵件的功能,預設情況下它會建立Content-Type型別為mulitpart/mixed郵件。
class MIMEMultipart(MIMEBase):
    """Base class for MIME multipart/* type messages."""

    def __init__(self, _subtype='mixed', boundary=None, _subparts=None,
                 **_params):
        """Creates a multipart/* type message.

        By default, creates a multipart/mixed message, with proper
        Content-Type and MIME-Version headers.

        _subtype is the subtype of the multipart content type, defaulting to
        `mixed'.

        boundary is the multipart boundary string.  By default it is
        calculated as needed.

        _subparts is a sequence of initial subparts for the payload.  It
        must be an iterable object, such as a list.  You can always
        attach new subparts to the message by using the attach() method.

        Additional parameters for the Content-Type header are taken from the
        keyword arguments (or passed into the _params argument).
        """
        MIMEBase.__init__(self, 'multipart', _subtype, **_params)

        # Initialise _payload to an empty list as the Message superclass's
        # implementation of is_multipart assumes that _payload is a list for
        # multipart messages.
        self._payload = []

        if _subparts:
            for p in _subparts:
                self.attach(p)
        if boundary:
            self.set_boundary(boundary)
在類初始化時,會將_payload初始化為空的列表,因為在Message超類中is_multipart方法假設_payload是一個列表,並用來存放多部分郵件內容,利用attach()方法可以將多部分郵件新增到列表中。這裡來看一下超類Message中的相關實現:
class Message:
	......

    def is_multipart(self):
        """Return True if the message consists of multiple parts."""
        return isinstance(self._payload, list)

    #
    # Payload manipulation.
    #
    def attach(self, payload):
        """Add the given payload to the current payload.

        The current payload will always be a list of objects after this method
        is called.  If you want to set the payload to a scalar object, use
        set_payload() instead.
        """
        if self._payload is None:
            self._payload = [payload]
        else:
            self._payload.append(payload)
			
	......

非多部分郵件類MIMENonMultipart類

MIMENonMultipart類與MIMEMultipart類一樣,繼承自MIMEBase基類,其實現了自定義attach()方法,由於其是非多部分郵件型別實現,使用者呼叫此方法,會丟擲MutlipartConversionError異常。
class MIMENonMultipart(MIMEBase):
    """Base class for MIME non-multipart type messages."""

    def attach(self, payload):
        # The public API prohibits attaching multiple subparts to MIMEBase
        # derived subtypes since none of them are, by definition, of content
        # type multipart/*
        raise errors.MultipartConversionError(
            'Cannot attach additional subparts to non-multipart/*')
MIMENonMultipart類是其它具體MIMEApplication, MIMEText,MIMEImage等其它類的基類,也就是說它們不執行使用者使用attach()方法。它們是通過set_payload()方法來實現設定郵件payload功能的。
class Message:
	......

    def set_payload(self, payload, charset=None):
        """Set the payload to the given value.

        Optional charset sets the message's default character set.  See
        set_charset() for details.
        """
        self._payload = payload
        if charset is not None:
            self.set_charset(charset)
			
	......

具體類實現

MIME模組提供了5個MIME具體類,各個具體類都提供了與名稱對應的主訊息型別的物件的支援,它們都繼承了MIMENonMultipart類;關於MIME主型別的知識,可以參考Python網路程式設計:E-mail服務(三)MIME詳解。這裡簡單看一下MIMEText的實現:
class MIMEText(MIMENonMultipart):
    """Class for generating text/* type MIME documents."""

    def __init__(self, _text, _subtype='plain', _charset='us-ascii'):
        """Create a text/* type MIME document.

        _text is the string for this message object.

        _subtype is the MIME sub content type, defaulting to "plain".

        _charset is the character set parameter added to the Content-Type
        header.  This defaults to "us-ascii".  Note that as a side-effect, the
        Content-Transfer-Encoding header will also be set.
        """
        MIMENonMultipart.__init__(self, 'text', _subtype,
                                  **{'charset': _charset})
        self.set_payload(_text, _charset)
MIMEText在初始化時,將主型別設定為text型別,並通過set_payload()函式設定郵件體內容。

總結

結合E-mail的核心類Message和MIME相關類的實現,可以更深入的瞭解通過email標準庫編寫郵件的機制,高效的實現編寫郵件相關的程式碼。