"email.message": 表示电子邮件消息
*********************************

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

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

Added in version 3.6: [1]

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

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

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

"EmailMessage" 字典型接口使用标头名称作为索引，它必须是 ASCII 值。字典
的值是包含一些附加方法的字符串。 标头是以保留大小写的形式存储和返回的
，但字段名的匹配则是大小写不敏感的。键是保留顺序的，但与真正字典不同的
是键可以重复。 提供了一些额外的方法来处理包含重复键的标头。

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

class email.message.EmailMessage(policy=default)

   如果指定了 *policy* 则使用它所指定的规则来更新和序列化消息的表示形
   式。如果没有设置 *policy*，则使用 "default" 策略，它将遵循电子邮件
   RFC 但行结束符除外（不像 RFC 要求用 "\r\n"，它将使用 Python 标准的
   "\n" 行结束符）。详情参见 "policy" 文档。 [2]

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

      以一段字符串的形式返回整个消息对象。若可选的 *unixfrom* 为真值，
      信封头将包括在返回的字符串中。 *unixfrom* 默认为 "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()" 返回 "False" 的场合下，载
      荷应当是一个字符串对象（有可能是一个使用了内容传输编码进行编码的
      二进制载荷）。请注意， "is_multipart()" 返回 "True" 不意味着
      "msg.get_content_maintype() == 'multipart'" 也会返回 "True" 。举
      个例子，"is_multipart" 在 "EmailMessage" 是 "message/rfc822" 类
      型的信息的情况下，其返回值也是 "True"。

   set_unixfrom(unixfrom)

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

   get_unixfrom()

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

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

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

   请注意在任何情况下，消息当中的任何封包标头都不会包含在映射接口当中
   。

   __len__()

      返回标头的总数，包括重复项。

   __contains__(name)

      如果消息对象中有一个名为 *name* 的字段，其返回值为 "True"。匹配
      无视大小写差异， *name* 也不包含末尾的冒号。"in" 操作符的实现中
      用到了这个方法，比如说:

         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* (*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" 之间选择。

      Added in version 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" 除错帮助函数可以帮助我们在下面这个例
      子当中看清楚这一点：

         >>> from email.iterators import _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()

      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()

      返回包含消息的所有直接子部分的迭代器，对于非 "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 来表示电子邮件消息
    .

[2] "EmailMessage" 类需要一个提供 "content_manager" 属性的策略以使
    "set_content()" 和 "get_content()" 等内容管理方法正常工作。旧式的
    "compat32" 策略不支持这些方法因此不应被用于 "EmailMessage"。
