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" 方法被调用的对象上寻找匹配的候选者。

      如果 "related" 未包括在 *preferencelist* 中，可考虑将所遇到的任
      意相关的根部分（或根部分的子部分）在该（子）部分与一个首选项相匹
      配时作为候选项。

      当遇到一个 "multipart/related" 时，将检查 "start" 形参并且如果找
      到了一个匹配 *Content-ID* 的部分，在查找候选匹配时只考虑它。 在
      其他情况下则只考虑 "multipart/related" 的第一个（默认的根）部分
      。

      如果一个部分具有 *Content-Disposition* 标头，则当标头值为
      "inline" 时将只考虑将该部分作为候选匹配。

      如果没有任何候选部分匹配 *preferencelist* 中的任何首选项，则返回
      "None"。

      注: (1) 对于大多数应用来说有意义的 *preferencelist* 组合仅有
      "('plain',)", "('html', 'plain')" 以及默认的 "('related',
      'html', 'plain')"。 (2) 由于匹配是从调用 "get_body" 的对象开始的
      ，因此在 "multipart/related" 上调用 "get_body" 将返回对象本身，
      除非 *preferencelist* 具有非默认值。 (3) 未指定 *Content-Type*
      或者 *Content-Type* 标头无效的消息（或消息部分）将被当作具有
      "text/plain" 类型来处理，这有时可能导致 "get_body" 返回非预期的
      结果。

   iter_attachments()

      返回包含所有不是候选 "body" 部分的消息的直接子部分的迭代器。 也
      就是说，跳过首次出现的每个 "text/plain", "text/html",
      "multipart/related" 或 "multipart/alternative" (除非通过
      *Content-Disposition: attachment* 将它们显式地标记为附件)，并返
      回所有的其余部分。 当直接应用于 "multipart/related" 时，将返回包
      含除根部分之外所有相关部分的迭代器（即由 "start" 形参所指向的部
      分，或者当没有 "start" 形参或 "start" 形参不能匹配任何部分的
      *Content-ID* 时则为第一部分）。 当直接应用于
      "multipart/alternative" 或非 "multipart" 时，将返回一个空迭代器
      。

   iter_parts()

      返回包含消息的所有直接子部分的迭代器，对于非 "multipart" 将为空
      对象。 （另请参阅 "walk()"。）

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

      调用 *content_manager* 的 "get_content()" 方法，将自身作为消息对
      象传入，并将其他参数或关键字作为额外参数传入。 如果未指定
      *content_manager*，则会使用当前 "policy" 所指定的
      "content_manager"。

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

      调用 *content_manager* 的 "set_content()" 方法，将自身作为消息传
      入，并将其他参数或关键字作为额外参数传入。 如果未指定
      *content_manager*，则会使用当前 "policy" 所指定的
      "content_manager"。

   make_related(boundary=None)

      将非 "multipart" 消息转换为 "multipart/related" 消息，将任何现有
      的 *Content-* 标头和载荷移入 "multipart" 的（新加）首部分。 如果
      指定了 *boundary*，会用它作为 multipart 中的分界字符串，否则会在
      必要时自动创建分界（例如当消息被序列化时）。

   make_alternative(boundary=None)

      将非 "multipart" 或 "multipart/related" 转换为
      "multipart/alternative"，将任何现有的 *Content-* 标头和载荷移入
      "multipart" 的（新加）首部分。 如果指定了 *boundary*，会用它作为
      multipart 中的分界字符串，否则会在必要时自动创建分界（例如当消息
      被序列化时）。

   make_mixed(boundary=None)

      将非 "multipart", "multipart/related" 或 "multipart-alternative"
      转换为 "multipart/mixed"，将任何现有的 *Content-* 标头和载荷移入
      "multipart" 的（新加）首部分。 如果指定了 *boundary*，会用它作为
      multipart 中的分界字符串，否则会在必要时自动创建分界（例如当消息
      被序列化时）。

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

      如果消息为 "multipart/related"，则创建一个新的消息对象，将所有参
      数传给其 "set_content()" 方法，并将其 "attach()" 到 "multipart"
      。 如果消息为非 "multipart"，则先调用 "make_related()" 然后再继
      续上述步骤。 如果消息为任何其他类型的 "multipart"，则会引发
      "TypeError"。 如果未指定 *content_manager*，则使用当前 "policy"
      所指定的 "content_manager"。 如果添加的部分没有 *Content-
      Disposition* 标头，则会添加一个值为 "inline" 的标头。

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

      如果消息为 "multipart/alternative"，则创建一个新的消息对象，将所
      有参数传给其 "set_content()" 方法，并将其 "attach()" 到
      "multipart"。 如果消息为非 "multipart" 或 "multipart/related"，
      则先调用 "make_alternative()" 然后再继续上述步骤。 如果消息为任
      何其他类型的 "multipart"，则会引发 "TypeError"。 如果未指定
      *content_manager*，则会使用当前 "policy" 所指定的
      "content_manager"。

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

      如果消息为 "multipart/mixed"，则创建一个新的消息对象，将所有参数
      传给其 "set_content()" 方法，并将其 "attach()" 到 "multipart"。
      如果消息为非 "multipart", "multipart/related" 或
      "multipart/alternative"，则先调用 "make_mixed()" 然后再继续上述
      步骤。 如果未指定 *content_manager*，则使用当前 "policy" 所指定
      的 "content_manager"。 如果添加的部分没有 *Content-Disposition*
      标头，则会添加一个值为 "attachment" 的标头。 此方法对于显式附件
      (*Content-Disposition: attachment*) 和 "inline" 附件 (*Content-
      Disposition: inline*) 均可使用，只须向 "content_manager" 传入适
      当的选项即可。

   clear()

      移除所有载荷和所有标头。

   clear_content()

      移除载荷以及所有 "Content-" 标头，其他标头不加改变并且保持其原有
      顺序。

   "EmailMessage" 对象具有下列实例属性:

   preamble

      MIME 文档格式在标头之后的空白行以及第一个多部分的分界字符串之间
      允许添加一些文本， 通常，此文本在支持 MIME 的邮件阅读器中永远不
      可见，因为它处在标准 MIME 防护范围之外。 但是，当查看消息的原始
      文本，或当在不支持 MIME 的阅读器中查看消息时，此文本会变得可见。

      *preamble* 属性包含 MIME 文档开头部分的这些处于保护范围之外的文
      本。 当 "Parser" 在标头之后及第一个分界字符串之前发现一些文本时
      ，它会将这些文本赋值给消息的 *preamble* 属性。 当 "Generator" 写
      出 MIME 消息的纯文本表示形式时，如果它发现消息具有 *preamble* 属
      性，它将在标头及第一个分界之间区域写出这些文本。 请参阅
      "email.parser" 和 "email.generator" 了解更多细节。

      请注意如果消息对象没有前导文本，则 *preamble* 属性将为 "None"。

   epilogue

      *epilogue* 属性的作用方式与 *preamble* 相同，区别在于它包含在最
      后一个分界及消息结尾之间出现的文本。 与 "preamble" 类似，如果没
      有附加文本，则此属性将为 "None"。

   defects

      *defects* 属性包含在解析消息时发现的所有问题的列表。 请参阅
      "email.errors" 了解可能的解析缺陷的详细描述。

class email.message.MIMEPart(policy=default)

   这个类代表 MIME 消息的子部分。 它与 "EmailMessage" 相似，不同之处在
   于当 "set_content()" 被调用时不会添加 *MIME-Version* 标头，因为子部
   分不需要有它们自己的 *MIME-Version* 标头。

-[ 备注 ]-

[1] 原先在3.4版本中以 *provisional module* 添加。过时的文档被移动至
    email.message.Message: 使用 compat32 API 来表示电子邮件消息 。
