19.1.1. "email.message": 表示一封电子邮件信息
*********************************************

**源代码:** Lib/email/message.py

======================================================================

3.6 新版功能: [1]

位于 "email" 包的中心的类就是 "EmailMessage" 类。这个类导入自
"email.message" 模块。它是 "email" 对象模型的基类。"EmailMessage" 为设
置和查询头字段内容、访问信息体的内容、以及创建和修改结构化信息提供了核
心功能。

一份电子邮件信息由*头*和*负载*（又被称为*内容*）组成。头遵循 **RFC
5322** 或者 **RFC 6532** 风格的字段名和值，字段名和字段值之间由一个冒
号隔开。这个冒号既不属于字段名，也不属于字段值。信息的负载可能是一段简
单的文字消息，也可能是一个二进制的对象，更可能是由多个拥有各自头和负载
的子信息组成的结构化子信息序列。对于后者类型的负载，信息的 MIME 类型将
会被指明为诸如 *multipart/** 或 *message/rfc822* 的类型。

"EmailMessage" 对象所提供的抽象概念模型是一个头字段组成的有序字典加一
个代表 **RFC 5322** 标准的信息体的*负载*。负载有可能是一系列子
``EmailMessage``对象的列表。你除了可以通过一般的字典方法来访问头字段名
和值，还可以使用特制方法来访问头的特定字段（比如说 MIME 内容类型字段）
、操纵负载、生成信息的序列化版本、递归遍历对象树。

"EmailMessage" 的类字典接口的字典索引是头字段名，头字段名必须是ASCII值
。字典值是带有一些附加方法的字符串。虽然头字段的存储和获取都是保留其原
始大小写的，但是字段名的匹配是大小写不敏感的。与真正的字典不同，键与键
之间不但存在顺序关系，还可以重复。我们提供了额外的方法来处理含有重复键
的头。

*payload* 是多样的。 对于简单的消息对象，它是字符串或字节串对象；对于
诸如 *multipart/** 和 *message/rfc822* 消息对象的 MIME 容器文档，它是
一个 "EmailMessage" 对象列表。

class email.message.EmailMessage(policy=default)

   如果指定了*policy*，消息将由这个*policy*所指定的规则来更新和序列化
   信息的表达。如果没有指定*policy*，其将默认使用 "default" 策略。这个
   策略遵循电子邮件的RFC标准，除了行终止符号（RFC要求使用``rn``，此策
   略使用Python标准的``n``行终止符）。请前往 "policy" 的文档获取更多信
   息。

   as_string(unixfrom=False, maxheaderlen=None, policy=None)

      以一段字符串的形式返回整个消息对象。 若可选的 *unixform* 参数为
      真，返回的字符串会包含信封头。 *unixform* 的默认值是 "False"。
      为了保持与基类 "Message" 的兼容性，*maxheaderlen* 是被接受的，但
      是其默认值是 "None"。 这个默认值表示行长度由策略的
      "max_line_length" 属性所控制。从信息实例所获取到的策略可以通过
      *policy* 参数重写。 这样可以对该方法所产生的输出进行略微的控制，
      因为指定的 *policy* 会被传递到 "Generator" 当中。

      扁平化信息可能会对 "EmailMessage" 做出修改。这是因为为了完成向字
      符串的转换，一些内容需要使用默认值填入（举个例子，MIME 边界字段
      可能会被生成或被修改）。

      请注意，这个方法是为了便利而提供，不一定是适合你的应用程序的最理
      想的序列化信息的方法。这在你处理多封信息的时候尤甚。如果你需要使
      用更加灵活的API来序列化信息，请参见 "email.generator.Generator"
      。同时请注意，当 "utf8" 属性为``False``的时候（这是默认值），本
      方法将限制其行为为生成以“7 bit clean”方式序列化的信息。

      在 3.6 版更改: *maxheaderlen*没有被指定时的默认行为从默认为0修改
      为默认为策略的*max_line_length*。

   __str__()

      与``as_string(policy=self.policy.clone(utf8=True))``等价。这将让
      ``str(msg)``产生的字符串包含人类可读的的序列化信息内容。

      在 3.4 版更改: 本方法开始使用``utf8=True``，而非 "as_string()"
      的直接替身。使用``utf8=True``会产生类似于 **RFC 6531** 的信息表
      达。

   as_bytes(unixfrom=False, policy=None)

      以字节串对象的形式返回整个扁平化后的消息。 当可选的 *unixfrom*
      为真值时，返回的字符串会包含信封标头。 *unixfrom* 的默认值为
      "False"。 *policy* 参数可被用于重载从消息实例获取的默认 policy。
      这可被用来控制该方法所产生的部分格式效果，因为指定的 *policy* 将
      被传递给 "BytesGenerator"。

      扁平化信息可能会对 "EmailMessage" 做出修改。这是因为为了完成向字
      符串的转换，一些内容需要使用默认值填入（举个例子，MIME 边界字段
      可能会被生成或被修改）。

      请注意，这个方法是为了便利而提供，不一定是适合你的应用程序的最理
      想的序列化信息的方法。这在你处理多封信息的时候尤甚。如果你需要使
      用更加灵活的API来序列化信息，请参见
      "email.generator.BytesGenerator" 。

   __bytes__()

      与 "as_bytes()" 等价。这将让``bytes(msg)``产生一个包含序列化信息
      内容的字节序列对象。

   is_multipart()

      如果该信息的负载是一个子 "EmailMessage" 对象列表，返回 "True" ；
      否则返回 "False" 。在 "is_multipart()" 返回 "True" 的场合下，负
      载应当是一个字符串对象（有可能是一个使用了内容传输编码进行编码的
      二进制负载）。请注意， "is_multipart()" 返回 "True" 不意味着
      "msg.get_content_maintype() == 'multipart'" 也会返回 "True" 。举
      个例子， "is_multipart" 在 "EmailMessage" 是 "message/rfc822" 类
      型的信息的情况下，其返回值也是 "True" 。

   set_unixfrom(unixfrom)

      将信息的信封头设置为 *unixform* ，这应当是一个字符串。（在
      "mboxMessage" 中有关于这个头的一段简短介绍。）

   get_unixfrom()

      返回消息的信封头。如果信封头从未被设置过，默认返回 "None" 。

   以下方法实现了对信息的头字段进行访问的类映射接口。请留意，只是类映
   射接口，这与平常的映射接口（比如说字典映射）有一些语义上的不同。举
   个例子，在一个字典当中，键之间不可重复，但是信息头字段是可以重复的
   。不光如此，在字典当中调用 "keys()" 方法返回的结果，其顺序没有保证
   ；但是在一个 "EmailMessage" 对象当中，返回的头字段永远以其在原信息
   当中出现的顺序，或以其加入信息的顺序为序。任何删了后又重新加回去的
   头字段总是添加在当时列表的末尾。

   这些语义上的不同是刻意而为之的，是出于在绝大多数常见使用情景中都方
   便的初衷下设计的。

   还请留意，无论在什么情况下，消息当中的任何信封头字段都不会包含在映
   射接口当中。

   __len__()

      返回头字段的总数，重复的也计算在内。

   __contains__(name)

      Return true if the message object has a field named *name*.
      Matching is done without regard to case and *name* does not
      include the trailing colon.  Used for the "in" operator.  For
      example:

         if 'message-id' in myMessage:
            print('Message-ID:', myMessage['message-id'])

   __getitem__(name)

      返回头字段名对应的字段值。 *name* 不含冒号分隔符。如果字段未找到
      ，返回 "None" 。 "KeyError" 异常永不抛出。

      请注意，如果对应名字的字段找到了多个，具体返回哪个字段值是未定义
      的。请使用 "get_all()" 方法获取匹配字段名的所有字段值。

      使用标准策略（非 "compat32"）时，返回值是
      "email.headerregistry.BaseHeader" 的某个子类的一个实例。

   __setitem__(name, val)

      在信息头中添加名为 *name* 值为 *val* 的字段。这个字段会被添加在
      已有字段列表的结尾处。

      请注意，这个方法 *既不会* 覆盖 *也不会* 删除任何字段名重名的已有
      字段。如果你确实想保证新字段是整个信息头当中唯一拥有 *name* 字段
      名的字段，你需要先把旧字段删除。例如：

         del msg['subject']
         msg['subject'] = 'Python roolz!'

      如果 "policy" 明确要求某些字段是唯一的（至少标准策略就有这么做）
      ，对这些字段在已有同名字段的情况下仍然尝试为字段名赋值会引发
      "ValueError" 异常。这是为了一致性而刻意设计出的行为，不过我们随
      时可能会突然觉得“还是在这种情况下自动把旧字段删除比较好吧”而把这
      个行为改掉，所以不要以为这是特性而依赖这个行为。

   __delitem__(name)

      删除信息头当中字段名匹配 *name* 的所有字段。如果匹配指定名称的字
      段没有找到，也不会抛出任何异常。

   keys()

      以列表形式返回消息头中所有的字段名。

   values()

      以列表形式返回消息头中所有的字段值。

   items()

      以二元元组的列表形式返回消息头中所有的字段名和字段值。

   get(name, failobj=None)

      返回对应字段名的字段值。这个方法与 "__getitem__()" 是一样的，只
      不过如果对应字段名的字段没有找到，该方法会返回 *failobj* 。这个
      参数是可选的（默认值为 "None" ）。

   以下是一些与头有关的更多有用方法：

   get_all(name, failobj=None)

      返回字段名为 *name* 的所有字段值的列表。如果信息内不存在匹配的字
      段，返回 *failobj* （其默认值为 "None" ）。

   add_header(_name, _value, **_params)

      高级头字段设定。这个方法与 "__setitem__()" 类似，不过你可以使用
      关键字参数为字段提供附加参数。 *_name* 是字段名， *_value* 是字
      段 *主* 值。

      对于关键字参数字典 *_params* 的每个键值对而言，它的键被用作参数
      的名字，其中下划线被替换为短横杠（毕竟短横杠不是合法的Python标识
      符）。一般来讲，参数以 "键="值"" 的方式添加，除非值是 "None" 。
      要真的是这样的话，只有键会被添加。

      如果值含有非ASCII字符，你可以将值写成 "(CHARSET, LANGUAGE,
      VALUE)" 形式的三元组，这样你可以人为控制字符的字符集和语言。
      "CHARSET" 是一个字符串，它为你的值的编码命名； "LANGUAGE" 一般可
      以直接设为 "None" ，也可以直接设为空字符串（其他可能取值参见
      :rfc`2231` ）； "`VALUE" 是一个字符串值，其包含非ASCII的码点。如
      果你没有使用三元组，你的字符串又含有非ASCII字符，那么它就会使用
      **RFC 2231** 中， "CHARSET" 为 "utf-8" ， "LANGUAGE" 为 "None"
      的格式编码。

      例如：

         msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

      会添加一个形如下文的头字段：

         Content-Disposition: attachment; filename="bud.gif"

      带有非ASCII字符的拓展接口：

         msg.add_header('Content-Disposition', 'attachment',
                        filename=('iso-8859-1', '', 'Fußballer.ppt'))

   replace_header(_name, _value)

      替换头字段。只会替换掉信息内找到的第一个字段名匹配 *_name* 的字
      段值。字段的顺序不变，原字段名的大小写也不变。如果没有找到匹配的
      字段，抛出 "KeyError" 异常。

   get_content_type()

      返回信息的内容类型，其形如 *maintype/subtype* ，强制全小写。如果
      信息的 *Content-Type* 头字段不存在则返回 "get_default_type()" 的
      返回值；如果信息的 *Content-Type* 头字段无效则返回 "text/plain"
      。

      （根据 **RFC 2045** 所述，信息永远都有一个默认类型，所以
      "get_content_type()" 一定会返回一个值。 **RFC 2045** 定义信息的
      默认类型为 *text/plain* 或 *message/rfc822* ，其中后者仅出现在消
      息头位于一个 *multipart/digest* 容器中的场合中。如果消息头的
      *Content-Type* 字段所指定的类型是无效的， **RFC 2045** 令其默认
      类型为 *text/plain* 。）

   get_content_maintype()

      返回信息的主要内容类型。准确来说，此方法返回的是
      "get_content_type()" 方法所返回的形如 *maintype/subtype* 的字符
      串当中的 *maintype* 部分。

   get_content_subtype()

      返回信息的子内容类型。准确来说，此方法返回的是
      "get_content_type()" 方法所返回的形如 *maintype/subtype* 的字符
      串当中的 *subtype* 部分。

   get_default_type()

      返回默认的内容类型。绝大多数的信息，其默认内容类型都是
      *text/plain* 。作为 *multipart/digest* 容器内子部分的信息除外，
      它们的默认内容类型是 *message/rfc822* 。

   set_default_type(ctype)

      设置默认的内容类型。 尽管并非强制，但是 *ctype* 仍应当是
      *text/plain* 或 *message/rfc822* 二者取一。默认内容类型并不存储
      在 *Content-Type* 头字段当中，所以设置此项的唯一作用就是决定当
      *Content-Type* 头字段在信息中不存在时，"get_content_type" 方法的
      返回值。

   set_param(param, value, header='Content-Type', requote=True, charset=None, language='', replace=False)

      在 *Content-Type* 头字段当中设置一个参数。如果该参数已于字段中存
      在，将其旧值替换为 *value* 。如果 *header* 是 "Content-Type" （
      默认值），并且该头字段于信息中尚未存在，则会先添加该字段，将其值
      设置为 *text/plain* ，并附加参数值。可选的 *header* 可以让你指定
      *Content-Type* 之外的另一个头字段。

      如果值包含非ASCII字符，其字符集和语言可以通过可选参数 *charset*
      和 *language* 显式指定。可选参数 *language* 指定 **RFC 2231** 当
      中的语言，其默认值是空字符串。 *charset* 和 *language* 都应当字
      符串。默认使用的是 "utf8" *charset* ，*language* 为 "None" 。

      如果 *replace* 为 "False" （默认值），该头字段会被移动到所有头字
      段的末尾。如果 *replace* 为 "True" ，字段会被原地更新。

      于 "EmailMessage" 对象而言， *requote* 参数已被弃用。

      请注意，头字段已有的参数值可以通过头字段的 "params" 属性来访问（
      举例： "msg['Content-Type'].params['charset']" ）。

      在 3.4 版更改: 添加了 "replace" 关键字。

   del_param(param, header='content-type', requote=True)

      从 *Content-Type* 头字段中完全移去给定的参数。头字段会被原地重写
      ，重写后的字段不含参数和值。可选的 *header* 可以让你指定
      *Content-Type* 之外的另一个字段。

      于 "EmailMessage" 对象而言， *requote* 参数已被弃用。

   get_filename(failobj=None)

      返回信息头当中 *Content-Disposition* 字段当中名为 "filename" 的
      参数值。如果该字段当中没有此参数，该方法会退而寻找 *Content-
      Type* 字段当中的 "name" 参数值。如果这个也没有找到，或者这些个字
      段压根就不存在，返回 *failobj* 。返回的字符串永远按照
      "email.utils.unquote()" 方法去除引号。

   get_boundary(failobj=None)

      返回信息头当中 *Content-Type* 字段当中名为 "boundary" 的参数值。
      如果字段当中没有此参数，或者这些个字段压根就不存在，返回
      *failobj* 。返回的字符串永远按照 "email.utils.unquote()" 方法去
      除引号。

   set_boundary(boundary)

      将 *Content-Type* 头字段的 "boundary" 参数设置为 *boundary* 。
      "set_boundary()" 方法永远都会在必要的时候为 *boundary* 添加引号
      。如果信息对象中没有 *Content-Type* 头字段，抛出
      "HeaderParseError" 异常。

      请注意使用这个方法与直接删除旧的 *Content-Type* 头字段然后使用
      "add_header()" 方法添加一个带有新边界值参数的 *Content-Type* 头
      字段有细微差距。 "set_boundary()" 方法会保留 *Content-Type* 头字
      段在原信息头当中的位置。

   get_content_charset(failobj=None)

      返回 *Content-Type* 头字段中的 "charset" 参数，强制小写。如果字
      段当中没有此参数，或者这个字段压根不存在，返回 *failobj* 。

   get_charsets(failobj=None)

      返回一个包含了信息内所有字符集名字的列表。如果信息是 *multipart*
      类型的，那么列表当中的每一项都对应其负载的子部分的字符集名字。否
      则，该列表是一个长度为1的列表。

      列表当中的每一项都是一个字符串，其值为对应子部分的 *Content-
      Type* 头字段的 "charset" 参数值。如果该子部分没有此头字段，或者
      没有此参数，或者其主要 MIME 类型并非 *text* ，那么列表中的那一项
      即为 *failobj* 。

   is_attachment()

      如果信息头当中存在一个名为 *Content-Disposition* 的字段，且该字
      段的值为 "attachment" （大小写无关），返回 "True" 。否则，返回
      "False" 。

      在 3.4.2 版更改: 为了与 "is_multipart()" 方法一致，is_attachment
      现在是一个方法，不再是属性了。

   get_content_disposition()

      如果信息的 *Content-Disposition* 头字段存在，返回其字段值；否则
      返回 "None" 。返回的值均为小写，不包含参数。如果信息遵循 **RFC
      2183** 标准，则返回值只可能在 *inline* 、 *attachment* 和 "None"
      之间选择。

      3.5 新版功能.

   下列方法与信息内容（负载）之访问与操控有关。

   walk()

      "walk()" 方法是一个多功能生成器。它可以被用来以深度优先顺序遍历
      信息对象树的所有部分和子部分。一般而言， "walk()" 会被用作 "for"
      循环的迭代器，每一次迭代都返回其下一个子部分。

      以下例子会打印出一封具有多部分结构之信息的每个部分的 MIME 类型。

         >>> for part in msg.walk():
         ...     print(part.get_content_type())
         multipart/report
         text/plain
         message/delivery-status
         text/plain
         text/plain
         message/rfc822
         text/plain

      "walk" 会遍历所有 "is_multipart()" 方法返回 "True" 的部分之子部
      分，哪怕 "msg.get_content_maintype() == 'multipart'" 返回的是
      "False" 。使用 "_structure" 除错帮助函数可以帮助我们在下面这个例
      子当中看清楚这一点：

         >>> for part in msg.walk():
         ...     print(part.get_content_maintype() == 'multipart',
         ...           part.is_multipart())
         True True
         False False
         False True
         False False
         False False
         False True
         False False
         >>> _structure(msg)
         multipart/report
             text/plain
             message/delivery-status
                 text/plain
                 text/plain
             message/rfc822
                 text/plain

      在这里， "message" 的部分并非 "multiparts" ，但是它们真的包含子
      部分！ "is_multipart()" 返回 "True" ， "walk" 也深入进这些子部分
      中。

   get_body(preferencelist=('related', 'html', 'plain'))

      返回信息的 MIME 部分。这个部分是最可能成为信息体的部分。

      *preferencelist* 必须是一个字符串序列，其内容从 "related" 、
      "html" 和 "plain" 这三者组成的集合中选取。这个序列代表着返回的部
      分的内容类型之偏好。

      在 "get_body" 方法被调用的对象上寻找匹配的候选者。

      If "related" is not included in *preferencelist*, consider the
      root part (or subpart of the root part) of any related
      encountered as a candidate if the (sub-)part matches a
      preference.

      When encountering a "multipart/related", check the "start"
      parameter and if a part with a matching *Content-ID* is found,
      consider only it when looking for candidate matches.  Otherwise
      consider only the first (default root) part of the
      "multipart/related".

      If a part has a *Content-Disposition* header, only consider the
      part a candidate match if the value of the header is "inline".

      If none of the candidates matches any of the preferences in
      *preferencelist*, return "None".

      Notes: (1) For most applications the only *preferencelist*
      combinations that really make sense are "('plain',)", "('html',
      'plain')", and the default "('related', 'html', 'plain')".  (2)
      Because matching starts with the object on which "get_body" is
      called, calling "get_body" on a "multipart/related" will return
      the object itself unless *preferencelist* has a non-default
      value. (3) Messages (or message parts) that do not specify a
      *Content-Type* or whose *Content-Type* header is invalid will be
      treated as if they are of type "text/plain", which may
      occasionally cause "get_body" to return unexpected results.

   iter_attachments()

      Return an iterator over all of the immediate sub-parts of the
      message that are not candidate "body" parts.  That is, skip the
      first occurrence of each of "text/plain", "text/html",
      "multipart/related", or "multipart/alternative" (unless they are
      explicitly marked as attachments via *Content-Disposition:
      attachment*), and return all remaining parts.  When applied
      directly to a "multipart/related", return an iterator over the
      all the related parts except the root part (ie: the part pointed
      to by the "start" parameter, or the first part if there is no
      "start" parameter or the "start" parameter doesn't match the
      *Content-ID* of any of the parts).  When applied directly to a
      "multipart/alternative" or a non-"multipart", return an empty
      iterator.

   iter_parts()

      Return an iterator over all of the immediate sub-parts of the
      message, which will be empty for a non-"multipart".  (See also
      "walk()".)

   get_content(*args, content_manager=None, **kw)

      Call the "get_content()" method of the *content_manager*,
      passing self as the message object, and passing along any other
      arguments or keywords as additional arguments.  If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   set_content(*args, content_manager=None, **kw)

      Call the "set_content()" method of the *content_manager*,
      passing self as the message object, and passing along any other
      arguments or keywords as additional arguments.  If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   make_related(boundary=None)

      Convert a non-"multipart" message into a "multipart/related"
      message, moving any existing *Content-* headers and payload into
      a (new) first part of the "multipart".  If *boundary* is
      specified, use it as the boundary string in the multipart,
      otherwise leave the boundary to be automatically created when it
      is needed (for example, when the message is serialized).

   make_alternative(boundary=None)

      Convert a non-"multipart" or a "multipart/related" into a
      "multipart/alternative", moving any existing *Content-* headers
      and payload into a (new) first part of the "multipart".  If
      *boundary* is specified, use it as the boundary string in the
      multipart, otherwise leave the boundary to be automatically
      created when it is needed (for example, when the message is
      serialized).

   make_mixed(boundary=None)

      Convert a non-"multipart", a "multipart/related", or a
      "multipart-alternative" into a "multipart/mixed", moving any
      existing *Content-* headers and payload into a (new) first part
      of the "multipart".  If *boundary* is specified, use it as the
      boundary string in the multipart, otherwise leave the boundary
      to be automatically created when it is needed (for example, when
      the message is serialized).

   add_related(*args, content_manager=None, **kw)

      If the message is a "multipart/related", create a new message
      object, pass all of the arguments to its "set_content()" method,
      and "attach()" it to the "multipart".  If the message is a
      non-"multipart", call "make_related()" and then proceed as
      above.  If the message is any other type of "multipart", raise a
      "TypeError". If *content_manager* is not specified, use the
      "content_manager" specified by the current "policy". If the
      added part has no *Content-Disposition* header, add one with the
      value "inline".

   add_alternative(*args, content_manager=None, **kw)

      If the message is a "multipart/alternative", create a new
      message object, pass all of the arguments to its "set_content()"
      method, and "attach()" it to the "multipart".  If the message is
      a non-"multipart" or "multipart/related", call
      "make_alternative()" and then proceed as above.  If the message
      is any other type of "multipart", raise a "TypeError". If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   add_attachment(*args, content_manager=None, **kw)

      If the message is a "multipart/mixed", create a new message
      object, pass all of the arguments to its "set_content()" method,
      and "attach()" it to the "multipart".  If the message is a
      non-"multipart", "multipart/related", or
      "multipart/alternative", call "make_mixed()" and then proceed as
      above. If *content_manager* is not specified, use the
      "content_manager" specified by the current "policy".  If the
      added part has no *Content-Disposition* header, add one with the
      value "attachment".  This method can be used both for explicit
      attachments (*Content-Disposition: attachment*) and "inline"
      attachments (*Content-Disposition: inline*), by passing
      appropriate options to the "content_manager".

   clear()

      Remove the payload and all of the headers.

   clear_content()

      Remove the payload and all of the "Content-" headers, leaving
      all other headers intact and in their original order.

   "EmailMessage" objects have the following instance attributes:

   preamble

      The format of a MIME document allows for some text between the
      blank line following the headers, and the first multipart
      boundary string. Normally, this text is never visible in a MIME-
      aware mail reader because it falls outside the standard MIME
      armor.  However, when viewing the raw text of the message, or
      when viewing the message in a non-MIME aware reader, this text
      can become visible.

      The *preamble* attribute contains this leading extra-armor text
      for MIME documents.  When the "Parser" discovers some text after
      the headers but before the first boundary string, it assigns
      this text to the message's *preamble* attribute.  When the
      "Generator" is writing out the plain text representation of a
      MIME message, and it finds the message has a *preamble*
      attribute, it will write this text in the area between the
      headers and the first boundary.  See "email.parser" and
      "email.generator" for details.

      Note that if the message object has no preamble, the *preamble*
      attribute will be "None".

   epilogue

      The *epilogue* attribute acts the same way as the *preamble*
      attribute, except that it contains text that appears between the
      last boundary and the end of the message.  As with the
      "preamble", if there is no epilog text this attribute will be
      "None".

   defects

      The *defects* attribute contains a list of all the problems
      found when parsing this message.  See "email.errors" for a
      detailed description of the possible parsing defects.

class email.message.MIMEPart(policy=default)

   This class represents a subpart of a MIME message.  It is identical
   to "EmailMessage", except that no *MIME-Version* headers are added
   when "set_content()" is called, since sub-parts do not need their
   own *MIME-Version* headers.

-[ 备注 ]-

[1] 原先在3.4版本中以 *provisional module* 添加。过时的文档被移动至
    email.message.Message: Representing an email message using the
    compat32 API 。
