当前位置: 首页 > news >正文

响应式网站原理晋城 网站建设

响应式网站原理,晋城 网站建设,wordpress伪静态404错误,南阳网站搭建源代码#xff1a; Lib/pickle.py 模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。 pickling 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程#xff0c;而 unpickling 是相反的操作#xff0c;会将#xff08… 源代码 Lib/pickle.py 模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。 pickling 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程而 unpickling 是相反的操作会将来自一个 binary file 或者 bytes-like object 的字节流转化回一个对象层次结构。 pickling和 unpickling也被称为“序列化”, “编组” 1 或者 “平面化”。而为了避免混乱此处采用术语 “封存 (pickling)” 和 “解封 (unpickling)”。 警告 pickle 模块 并不安全。 你只应该对你信任的数据进行 unpickle 操作。 构建恶意的 pickle 数据来 在解封时执行任意代码 是可能的。 绝对不要对不信任来源的数据和可能被篡改过的数据进行解封。 请考虑使用 hmac 来对数据进行签名确保数据没有被篡改。 在你处理不信任数据时更安全的序列化格式如 json 可能更为适合。参见 与 json 模块的比较 。 与其他 Python 模块间的关系 与 marshal 间的关系 Python 有一个更原始的序列化模块称为 marshal但一般地 pickle 应该是序列化 Python 对象时的首选。marshal 存在主要是为了支持 Python 的 .pyc 文件. pickle 模块与 marshal 在如下几方面显著地不同 pickle 模块会跟踪已被序列化的对象所以该对象之后再次被引用时不会再次被序列化。marshal 不会这么做。 这隐含了递归对象和共享对象。递归对象指包含对自己的引用的对象。这种对象并不会被 marshal 接受并且实际上尝试 marshal 递归对象会让你的 Python 解释器崩溃。对象共享发生在对象层级中存在多处引用同一对象时。pickle 只会存储这些对象一次并确保其他的引用指向同一个主副本。共享对象将保持共享这可能对可变对象非常重要。 marshal 不能被用于序列化用户定义类及其实例。pickle 能够透明地存储并保存类实例然而此时类定义必须能够从与被存储时相同的模块被引入。 同样用于序列化的 marshal 格式不保证数据能移植到不同的 Python 版本中。因为它的主要任务是支持 .pyc 文件必要时会以破坏向后兼容的方式更改这种序列化格式为此 Python 的实现者保留了更改格式的权利。pickle 序列化格式可以在不同版本的 Python 中实现向后兼容前提是选择了合适的 pickle 协议。如果你的数据要在 Python 2 与 Python 3 之间跨越传递封存和解封的代码在 2 和 3 之间也是不同的。 与 json 模块的比较 在 pickle 协议和 JSON (JavaScript Object Notation) 之间有着本质上的差异: JSON 是一个文本序列化格式它输出 unicode 文本尽管在大多数时候它会接着以 utf-8 编码而 pickle 是一个二进制序列化格式 JSON 是我们可以直观阅读的而 pickle 不是 JSON是可互操作的在Python系统之外广泛使用而pickle则是Python专用的 默认情况下JSON 只能表示 Python 内置类型的子集不能表示自定义的类但 pickle 可以表示大量的 Python 数据类型可以合理使用 Python 的对象内省功能自动地表示大多数类型复杂情况可以通过实现 specific object APIs 来解决。 不像pickle对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞。 参见 json 模块:一个允许JSON序列化和反序列化的标准库模块 数据流格式 pickle 所使用的数据格式仅可用于 Python。这样做的好处是没有外部标准给该格式强加限制比如 JSON 或 XDR不能表示共享指针标准但这也意味着非 Python 程序可能无法重新读取 pickle 封存的 Python 对象。 默认情况下pickle 格式使用相对紧凑的二进制来存储。如果需要让文件更小可以高效地 压缩 由 pickle 封存的数据。 pickletools 模块包含了相应的工具用于分析 pickle 生成的数据流。pickletools 源码中包含了对 pickle 协议使用的操作码的大量注释。 当前共有 6 种不同的协议可用于封存操作。 使用的协议版本越高读取所生成 pickle 对象所需的 Python 版本就要越新。 v0 版协议是原始的“人类可读”协议并且向后兼容早期版本的 Python。 v1 版协议是较早的二进制格式它也与早期版本的 Python 兼容。 第 2 版协议是在 Python 2.3 中引入的。 它为 新式类 提供了更高效的封存机制。 请参考 PEP 307 了解第 2 版协议带来的改进的相关信息。 v3 版协议是在 Python 3.0 中引入的。 它显式地支持 bytes 字节对象不能使用 Python 2.x 解封。这是 Python 3.0-3.7 的默认协议。 v4 版协议添加于 Python 3.4。它支持存储非常大的对象能存储更多种类的对象还包括一些针对数据格式的优化。它是Python 3.8使用的默认协议。有关第 4 版协议带来改进的信息请参阅 PEP 3154。 第 5 版协议是在 Python 3.8 中加入的。 它增加了对带外数据的支持并可加速带内数据处理。 请参阅 PEP 574 了解第 5 版协议所带来的改进的详情。 备注 序列化是一种比持久化更底层的概念虽然 pickle 读取和写入的是文件对象但它不处理持久对象的命名问题也不处理对持久对象的并发访问甚至更复杂的问题。pickle 模块可以将复杂对象转换为字节流也可以将字节流转换为具有相同内部结构的对象。处理这些字节流最常见的做法是将它们写入文件但它们也可以通过网络发送或存储在数据库中。shelve 模块提供了一个简单的接口用于在 DBM 类型的数据库文件上封存和解封对象。 模块接口 要序列化某个包含层次结构的对象只需调用 dumps() 函数即可。同样要反序列化数据流可以调用 loads() 函数。但是如果要对序列化和反序列化加以更多的控制可以分别创建 Pickler 或 Unpickler 对象。 pickle 模块包含了以下常量 pickle.HIGHEST_PROTOCOL 整数可用的最高 协议版本。此值可以作为 协议 值传递给 dump() 和 dumps() 函数以及 Pickler 的构造函数。 pickle.DEFAULT_PROTOCOL 整数用于 pickle 数据的默认 协议版本。它可能小于 HIGHEST_PROTOCOL。当前默认协议是 v4它在 Python 3.4 中首次引入与之前的版本不兼容。 在 3.0 版更改: 默认协议版本是 3。 在 3.8 版更改: 默认协议版本是 4。 pickle 模块提供了以下方法让封存过程更加方便 pickle.dump(obj, file, protocolNone, *, fix_importsTrue, buffer_callbackNone) 将对象 obj 封存以后的对象写入已打开的 file object file。它等同于 Pickler(file, protocol).dump(obj)。 参数 file、protocol、fix_imports 和 buffer_callback 的含义与它们在 Pickler 的构造函数中的含义相同。 在 3.8 版更改: 加入了 buffer_callback 参数。 pickle.dumps(obj, protocolNone, *, fix_importsTrue, buffer_callbackNone) 将 obj 封存以后的对象作为 bytes 类型直接返回而不是将其写入到文件。 参数 protocol、fix_imports 和 buffer_callback 的含义与它们在 Pickler 的构造函数中的含义相同。 在 3.8 版更改: 加入了 buffer_callback 参数。 pickle.load(file, *, fix_importsTrue, encodingASCII, errorsstrict, buffersNone) 从已打开的 file object 文件 中读取封存后的对象重建其中特定对象的层次结构并返回。它相当于 Unpickler(file).load()。 Pickle 协议版本是自动检测出来的所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。 参数 file、fix_imports、encoding、errors、strict 和 buffers 的含义与它们在 Unpickler 的构造函数中的含义相同。 在 3.8 版更改: 加入了 buffers 参数。 pickle.loads(data, /, *, fix_importsTrue, encodingASCII, errorsstrict, buffersNone) 重建并返回一个对象的封存表示形式 data 的对象层级结构。 data 必须为 bytes-like object。 Pickle 协议版本是自动检测出来的所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。 参数 fix_imports, encoding, errors, strict 和 buffers 的含义与在 Unpickler 构造器中的含义相同。 在 3.8 版更改: 加入了 buffers 参数。 pickle 模块定义了以下 3 个异常 exception pickle.PickleError 其他 pickle 异常的基类。它是 Exception 的一个子类。 exception pickle.PicklingError 当 Pickler 遇到无法解封的对象时抛出此错误。它是 PickleError 的子类。 参考 可以被封存/解封的对象 来了解哪些对象可以被封存。 exception pickle.UnpicklingError 当解封出错时抛出此异常例如数据损坏或对象不安全。它是 PickleError 的子类。 注意解封时可能还会抛出其他异常包括但不限于 AttributeError、EOFError、ImportError 和 IndexError。 pickle 模块包含了 3 个类Pickler、Unpickler 和 PickleBuffer class pickle.Pickler(file, protocolNone, *, fix_importsTrue, buffer_callbackNone) 它接受一个二进制文件用于写入 pickle 数据流。 可选参数 protocol 是一个整数告知 pickler 使用指定的协议可选择的协议范围从 0 到 HIGHEST_PROTOCOL。如果没有指定这一参数默认值为 DEFAULT_PROTOCOL。指定一个负数就相当于指定 HIGHEST_PROTOCOL。 参数 file 必须有一个 write() 方法该 write() 方法要能接收字节作为其唯一参数。因此它可以是一个打开的磁盘文件用于写入二进制内容也可以是一个 io.BytesIO 实例也可以是满足这一接口的其他任何自定义对象。 如果 fix_imports 为 True 且 protocol 小于 3pickle 将尝试将 Python 3 中的新名称映射到 Python 2 中的旧模块名称因此 Python 2 也可以读取封存的数据流。 如果 buffer_callback 为 None默认情况缓冲区视图buffer view将会作为 pickle 流的一部分被序列化到 file 中。 如果 buffer_callback 不为 None那它可以用缓冲区视图调用任意次。如果某次调用返回了 False 值例如 None则给定的缓冲区是 带外的否则缓冲区是带内的例如保存在了 pickle 流里面。 如果 buffer_callback 不是 None 且 protocol 是 None 或小于 5就会出错。 在 3.8 版更改: 加入了 buffer_callback 参数。 dump(obj) 将 obj 封存后的内容写入已打开的文件对象该文件对象已经在构造函数中指定。 persistent_id(obj) 默认无动作子类继承重载时使用。 如果 persistent_id() 返回 Noneobj 会被照常 pickle。如果返回其他值Pickler 会将这个函数的返回值作为 obj 的持久化 IDPickler 本应得到序列化数据流并将其写入文件若此函数有返回值则得到此函数的返回值并写入文件。这个持久化 ID 的解释应当定义在 Unpickler.persistent_load() 中该方法定义还原对象的过程并返回得到的对象。注意persistent_id() 的返回值本身不能拥有持久化 ID。 参阅 持久化外部对象 获取详情和使用示例。 dispatch_table Pickler 对象的 dispatch 表是 copyreg.pickle() 中用到的 reduction 函数 的注册。dispatch 表本身是一个 class 到其 reduction 函数的映射键值对。一个 reduction 函数只接受一个参数就是其关联的 class函数行为应当遵守 __reduce__() 接口规范。 Pickler 对象默认并没有 dispatch_table 属性该对象默认使用 copyreg 模块中定义的全局 dispatch 表。如果要为特定 Pickler 对象自定义序列化过程可以将 dispatch_table 属性设置为类字典对象dict-like object。另外如果 Pickler 的子类设置了 dispatch_table 属性则该子类的实例会使用这个表作为默认的 dispatch 表。 参阅 Dispatch 表 获取使用示例。 3.3 新版功能. reducer_override(obj) 可以在 Pickler 的子类中定义的特殊 reducer。此方法的优先级高于 dispatch_table 中的任何 reducer。它应该与 __reduce__() 方法遵循相同的接口它也可以返回 NotImplemented这将使用 dispatch_table 里注册的 reducer 来封存 obj。 参阅 类型函数和其他对象的自定义归约 获取详细的示例。 3.8 新版功能. fast 已弃用。设为 True 则启用快速模式。快速模式禁用了“备忘录” (memo) 的使用即不生成多余的 PUT 操作码来加快封存过程。不应将其与自指 (self-referential) 对象一起使用否则将导致 Pickler 无限递归。 如果需要进一步提高 pickle 的压缩率请使用 pickletools.optimize()。 class pickle.Unpickler(file, *, fix_importsTrue, encodingASCII, errorsstrict, buffersNone) 它接受一个二进制文件用于读取 pickle 数据流。 Pickle 协议版本是自动检测出来的所以不需要参数来指定协议。 参数 file 必须有三个方法read() 方法接受一个整数参数readinto() 方法接受一个缓冲区作为参数readline() 方法不需要参数这与 io.BufferedIOBase 里定义的接口是相同的。因此 file 可以是一个磁盘上用于二进制读取的文件也可以是一个 io.BytesIO 实例也可以是满足这一接口的其他任何自定义对象。 可选的参数是 fix_imports, encoding 和 errors用于控制由Python 2 生成的 pickle 流的兼容性。如果 fix_imports 为 True则 pickle 将尝试将旧的 Python 2 名称映射到 Python 3 中对应的新名称。encoding 和 errors 参数告诉 pickle 如何解码 Python 2 存储的 8 位字符串实例这两个参数默认分别为 ASCII 和 strict。encoding 参数可置为 bytes 来将这些 8 位字符串实例读取为字节对象。读取 NumPy array 和 Python 2 存储的 datetime、date 和 time 实例时请使用 encodinglatin1。 如果 buffers 为 None默认值则反序列化所需的所有数据都必须包含在 pickle 流中。这意味着在实例化 Pickler 时或调用 dump() 或 dumps() 时参数 buffer_callback 为 None。 如果 buffers 不为 None则每次 pickle 流引用 带外 缓冲区视图时消耗的对象都应该是可迭代的启用缓冲区的对象。这样的缓冲区应该按顺序地提供给 Pickler 对象的 buffer_callback 方法。 在 3.8 版更改: 加入了 buffers 参数。 load() 从构造函数中指定的文件对象里读取封存好的对象重建其中特定对象的层次结构并返回。封存对象以外的其他字节将被忽略。 persistent_load(pid) 默认抛出 UnpicklingError 异常。 如果定义了此方法persistent_load() 应当返回持久化 ID pid 所指定的对象。 如果遇到无效的持久化 ID则应当引发 UnpicklingError。 参阅 持久化外部对象 获取详情和使用示例。 find_class(module, name) 如有必要导入 module 模块并返回其中名叫 name 的对象其中 module 和 name 参数都是 str 对象。注意不要被这个函数的名字迷惑 find_class() 同样可以用来导入函数。 子类可以重载此方法来控制加载对象的类型和加载对象的方式从而尽可能降低安全风险。参阅 限制全局变量 获取更详细的信息。 引发一个 审计事件 pickle.find_class 附带参数 module、name。 class pickle.PickleBuffer(buffer) 缓冲区的包装器 (wrapper)缓冲区中包含着可封存的数据。buffer 必须是一个 buffer-providing 对象比如 bytes-like object 或多维数组。 PickleBuffer 本身就可以生成缓冲区对象因此可以将其传递给需要缓冲区生成器的其他 API比如 memoryview。 PickleBuffer 对象只能用 pickle 版本 5 及以上协议进行序列化。它们符合 带外序列化 的条件。 3.8 新版功能. raw() 返回该缓冲区底层内存区域的 memoryview。 返回的对象是一维的、C 连续布局的 memoryview格式为 B (无符号字节)。 如果缓冲区既不是 C 连续布局也不是 Fortran 连续布局的则抛出 BufferError 异常。 release() 释放由 PickleBuffer 占用的底层缓冲区。 可以被封存/解封的对象 下列类型可以被封存 内置常量 (None, True, False, Ellipsis 和 NotImplemented) 整数、浮点数、复数; 字符串、字节串、字节数组; 只包含可封存对象的元组、列表、集合和字典; 可在模块最高层级上访问的内置与用户自定义的函数使用 def而不是使用 lambda 定义; 可在模块最高层级上访问的类; 这种类的实例调用 __getstate__() 的结果是可 pickle 的请参阅 封存类实例 了解详情。 尝试封存不能被封存的对象会抛出 PicklingError 异常异常发生时可能有部分字节已经被写入指定文件中。尝试封存递归层级很深的对象时可能会超出最大递归层级限制此时会抛出 RecursionError 异常可以通过 sys.setrecursionlimit() 调整递归层级不过请谨慎使用这个函数因为可能会导致解释器崩溃。 请注意内置与用户自定义的函数是按完整 qualified name而不是按值来封存的。 2 这意味着只会封存函数名称以及包含它的模块和类名称。 函数的代码以及函数的属性都不会被封存。 因而定义它的模块在解封环境中必须可以被导入并且模块必须包含所命名的对象否则将会引发异常。 3 类似地类也是按完整限定名称来封存的因此在解封环境中也会应用相同的限制。 请注意类的代码或数据都不会被封存因此在下面的示例中类属性 attr 不会在解封环境中被恢复: class Foo:attr A class attributepicklestring pickle.dumps(Foo)这些限制决定了为什么可封存的函数和类必须在一个模块的最高层级上定义。 类似的在封存类的实例时其类体和类数据不会跟着实例一起被封存只有实例数据会被封存。这样设计是有目的的在将来修复类中的错误、给类增加方法之后仍然可以载入原来版本类实例的封存数据来还原该实例。如果你准备长期使用一个对象可能会同时存在较多版本的类体可以为对象添加版本号这样就可以通过类的 __setstate__() 方法将老版本转换成新版本。 封存类实例 在本节中我们描述了可用于定义、自定义和控制如何封存和解封类实例的通用流程。 通常使一个实例可被封存不需要附加任何代码。Pickle 默认会通过 Python 的内省机制获得实例的类及属性。而当实例解封时它的 __init__() 方法通常 不会 被调用。其默认动作是先创建一个未初始化的实例然后还原其属性下面的代码展示了这种行为的实现机制 def save(obj):return (obj.__class__, obj.__dict__)def restore(cls, attributes):obj cls.__new__(cls)obj.__dict__.update(attributes)return obj类可以改变默认行为只需定义以下一种或几种特殊方法 object.__getnewargs_ex__() 对于使用第 2 版或更高版协议的 pickle实现了 __getnewargs_ex__() 方法的类可以控制在解封时传给 __new__() 方法的参数。本方法必须返回一对 (args, kwargs) 用于构建对象其中 args 是表示位置参数的 tuple而 kwargs 是表示命名参数的 dict。它们会在解封时传递给 __new__() 方法。 如果类的 __new__() 方法只接受关键字参数则应当实现这个方法。否则为了兼容性更推荐实现 __getnewargs__() 方法。 在 3.6 版更改: __getnewargs_ex__() 现在可用于第 2 和第 3 版协议。 object.__getnewargs__() 这个方法与上一个 __getnewargs_ex__() 方法类似但仅支持位置参数。它要求返回一个 tuple 类型的 args用于解封时传递给 __new__() 方法。 如果定义了 __getnewargs_ex__()那么 __getnewargs__() 就不会被调用。 在 3.6 版更改: 在 Python 3.6 前第 2、3 版协议会调用 __getnewargs__()更高版本协议会调用 __getnewargs_ex__()。 object.__getstate__() 类还可以通过重载方法 __getstate__() 来进一步影响它们的实例要如何被封存。 该方法将被调用并且其返回的对象会被当作实例的内容来封存而不是使用默认状态。 这有几种情况: 对于没有实例 __dict__ 以及没有 __slots__ 的类默认状态为 None。 对于具有实例 __dict__ 而没有 __slots__ 的类默认状态为 self.__dict__。 对于具有实例 __dict__ 和 __slots__ 的类默认状态为一个由两个字典: self.__dict__、以及将槽位名称映射到槽位值的字典所组成的元组。 只有包含具体值的槽位才会被包括在后一个字典当中。 对于具有 __slots__ 而没有实例 __dict__ 的类默认状态为一个第一项是 None 而第二项是上述将槽位名称映射到槽位值的字典的元组。 在 3.11 版更改: 将 __getstate__() 方法的默认实现添加到 object 类中。 object.__setstate__(state) 当解封时如果类定义了 __setstate__()就会在已解封状态下调用它。此时不要求实例的 state 对象必须是 dict。没有定义此方法的话先前封存的 state 对象必须是 dict且该 dict 内容会在解封时赋给新实例的 __dict__。 备注 如果 __getstate__() 返回 False那么在解封时就不会调用 __setstate__() 方法。 参考 处理有状态的对象 一段获取如何使用 __getstate__() 和 __setstate__() 方法的更多信息。 备注 在解封时实例的某些方法例如 __getattr__(), __getattribute__() 或 __setattr__() 可能会被调用。 由于这些方法可能要求某些内部不变量为真值因此该类型应当实现 __new__() 以建立这样的不变量因为当解封一个实例时 __init__() 并不会被调用。 可以看出其实 pickle 并不直接调用上面的几个函数。事实上这几个函数是复制协议的一部分它们实现了 __reduce__() 这一特殊接口。复制协议提供了统一的接口用于在封存或复制对象的过程中取得所需数据。4 尽管这个协议功能很强但是直接在类中实现 __reduce__() 接口容易产生错误。因此设计类时应当尽可能的使用高级接口比如 __getnewargs_ex__()、__getstate__() 和 __setstate__()。后面仍然可以看到直接实现 __reduce__() 接口的状况可能别无他法可能为了获得更好的性能或者两者皆有之。 object.__reduce__() 该接口当前定义如下。__reduce__() 方法不带任何参数并且应返回字符串或最好返回一个元组返回的对象通常称为“reduce 值”。 如果返回字符串该字符串会被当做一个全局变量的名称。它应该是对象相对于其模块的本地名称pickle 模块会搜索模块命名空间来确定对象所属的模块。这种行为常在单例模式使用。 如果返回的是元组则应当包含 2 到 6 个元素可选元素可以省略或设置为 None。每个元素代表的意义如下 一个可调用对象该对象会在创建对象的最初版本时调用。 可调用对象的参数是一个元组。如果可调用对象不接受参数必须提供一个空元组。 可选元素用于表示对象的状态将被传给前述的 __setstate__() 方法。 如果对象没有此方法则这个元素必须是字典类型并会被添加至 __dict__ 属性中。 可选元素一个返回连续项的迭代器而不是序列。这些项会被 obj.append(item) 逐个加入对象或被 obj.extend(list_of_items) 批量加入对象。这个元素主要用于 list 的子类也可以用于那些正确实现了 append() 和 extend() 方法的类。具体是使用 append() 还是 extend() 取决于 pickle 协议版本以及待插入元素的项数所以这两个方法必须同时被类支持。 可选元素一个返回连续键值对的迭代器而不是序列。这些键值对将会以 obj[key]  value 的方式存储于对象中。该元素主要用于 dict 子类也可以用于那些实现了 __setitem__() 的类。 可选元素一个带有 (obj, state) 签名的可调用对象。该可调用对象允许用户以编程方式控制特定对象的状态更新行为而不是使用 obj 的静态 __setstate__() 方法。如果此处不是 None则此可调用对象的优先级高于 obj 的 __setstate__()。 3.8 新版功能: 新增了元组的第 6 项可选元素 (obj, state)。 object.__reduce_ex__(protocol) 作为替代选项也可以实现 __reduce_ex__() 方法。 此方法的唯一不同之处在于它应接受一个整型参数用于指定协议版本。 如果定义了这个函数则会覆盖 __reduce__() 的行为。 此外__reduce__() 方法会自动成为扩展版方法的同义词。 这个函数主要用于为以前的 Python 版本提供向后兼容的 reduce 值。 持久化外部对象 为了获取对象持久化的利益 pickle 模块支持引用已封存数据流之外的对象。 这样的对象是通过一个持久化 ID 来引用的它应当是一个由字母数字类字符组成的字符串 (对于第 0 版协议) 5 或是一个任意对象 (用于任意新版协议)。 pickle 模块不提供对持久化 ID 的解析工作它将解析工作分配给用户定义的方法分别是 pickler 中的 persistent_id() 方法和 unpickler 中的 persistent_load() 方法。 要通过持久化 ID 将外部对象封存必须在 pickler 中实现 persistent_id() 方法该方法接受需要被封存的对象作为参数返回一个 None 或返回该对象的持久化 ID。如果返回 None该对象会被按照默认方式封存为数据流。如果返回字符串形式的持久化 ID则会封存这个字符串并加上一个标记这样 unpickler 才能将其识别为持久化 ID。 要解封外部对象Unpickler 必须实现 persistent_load() 方法接受一个持久化 ID 对象作为参数并返回一个引用的对象。 下面是一个全面的例子展示了如何使用持久化 ID 来封存外部对象。 # Simple example presenting how persistent ID can be used to pickle # external objects by reference.import pickle import sqlite3 from collections import namedtuple# Simple class representing a record in our database. MemoRecord namedtuple(MemoRecord, key, task)class DBPickler(pickle.Pickler):def persistent_id(self, obj):# Instead of pickling MemoRecord as a regular class instance, we emit a# persistent ID.if isinstance(obj, MemoRecord):# Here, our persistent ID is simply a tuple, containing a tag and a# key, which refers to a specific record in the database.return (MemoRecord, obj.key)else:# If obj does not have a persistent ID, return None. This means obj# needs to be pickled as usual.return Noneclass DBUnpickler(pickle.Unpickler):def __init__(self, file, connection):super().__init__(file)self.connection connectiondef persistent_load(self, pid):# This method is invoked whenever a persistent ID is encountered.# Here, pid is the tuple returned by DBPickler.cursor self.connection.cursor()type_tag, key_id pidif type_tag MemoRecord:# Fetch the referenced record from the database and return it.cursor.execute(SELECT * FROM memos WHERE key?, (str(key_id),))key, task cursor.fetchone()return MemoRecord(key, task)else:# Always raises an error if you cannot return the correct object.# Otherwise, the unpickler will think None is the object referenced# by the persistent ID.raise pickle.UnpicklingError(unsupported persistent object)def main():import ioimport pprint# Initialize and populate our database.conn sqlite3.connect(:memory:)cursor conn.cursor()cursor.execute(CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT))tasks (give food to fish,prepare group meeting,fight with a zebra,)for task in tasks:cursor.execute(INSERT INTO memos VALUES(NULL, ?), (task,))# Fetch the records to be pickled.cursor.execute(SELECT * FROM memos)memos [MemoRecord(key, task) for key, task in cursor]# Save the records using our custom DBPickler.file io.BytesIO()DBPickler(file).dump(memos)print(Pickled records:)pprint.pprint(memos)# Update a record, just for good measure.cursor.execute(UPDATE memos SET tasklearn italian WHERE key1)# Load the records from the pickle data stream.file.seek(0)memos DBUnpickler(file, conn).load()print(Unpickled records:)pprint.pprint(memos)if __name__ __main__:main()Dispatch 表 如果想对某些类进行自定义封存而又不想在类中增加用于封存的代码就可以创建带有特殊 dispatch 表的 pickler。 在 copyreg 模块的 copyreg.dispatch_table 中定义了全局 dispatch 表。因此可以使用 copyreg.dispatch_table 修改后的副本作为自有 dispatch 表。 例如 f io.BytesIO() p pickle.Pickler(f) p.dispatch_table copyreg.dispatch_table.copy() p.dispatch_table[SomeClass] reduce_SomeClass创建了一个带有自有 dispatch 表的 pickle.Pickler 实例它可以对 SomeClass 类进行特殊处理。另外下列代码 class MyPickler(pickle.Pickler):dispatch_table copyreg.dispatch_table.copy()dispatch_table[SomeClass] reduce_SomeClass f io.BytesIO() p MyPickler(f)完成同样的操作但所有 MyPickler 的实例都会共享一个私有分发表。 另一方面代码 copyreg.pickle(SomeClass, reduce_SomeClass) f io.BytesIO() p pickle.Pickler(f)会修改由 copyreg 模块的所有用户共享的全局分发表。 处理有状态的对象 下面的示例展示了如何修改类在封存时的行为。其中 TextReader 类打开了一个文本文件每次调用其 readline() 方法则返回行号和该行的字符。 在封存这个 TextReader 的实例时除了 文件对象其他属性都会被保存。 当解封实例时需要重新打开文件然后从上次的位置开始继续读取。实现这些功能需要实现 __setstate__() 和 __getstate__() 方法。 class TextReader:Print and number lines in a text file.def __init__(self, filename):self.filename filenameself.file open(filename)self.lineno 0def readline(self):self.lineno 1line self.file.readline()if not line:return Noneif line.endswith(\n):line line[:-1]return %i: %s % (self.lineno, line)def __getstate__(self):# Copy the objects state from self.__dict__ which contains# all our instance attributes. Always use the dict.copy()# method to avoid modifying the original state.state self.__dict__.copy()# Remove the unpicklable entries.del state[file]return statedef __setstate__(self, state):# Restore instance attributes (i.e., filename and lineno).self.__dict__.update(state)# Restore the previously opened files state. To do so, we need to# reopen it and read from it until the line count is restored.file open(self.filename)for _ in range(self.lineno):file.readline()# Finally, save the file.self.file file使用方法如下所示 reader TextReader(hello.txt)reader.readline() 1: Hello world!reader.readline() 2: I am line number two.new_reader pickle.loads(pickle.dumps(reader))new_reader.readline() 3: Goodbye!类型函数和其他对象的自定义归约 3.8 新版功能. 有时dispatch_table 可能不够灵活。 特别是当我们想要基于对象类型以外的其他规则来对封存进行定制或是当我们想要对函数和类的封存进行定制的时候。 对于那些情况可能要基于 Pickler 类进行子类化并实现 reducer_override() 方法。 此方法可返回任意的归约元组 (参见 __reduce__())。 它也可以选择返回 NotImplemented 来回退到传统行为。 如果同时定义了 dispatch_table 和 reducer_override()则 reducer_override() 方法具有优先权。 备注 出于性能理由可能不会为以下对象调用 reducer_override(): None, True, False, 以及 int, float, bytes, str, dict, set, frozenset, list 和 tuple 的具体实例。 以下是一个简单的例子其中我们允许封存并重新构建一个给定的类: import io import pickleclass MyClass:my_attribute 1class MyPickler(pickle.Pickler):def reducer_override(self, obj):Custom reducer for MyClass.if getattr(obj, __name__, None) MyClass:return type, (obj.__name__, obj.__bases__,{my_attribute: obj.my_attribute})else:# For any other object, fallback to usual reductionreturn NotImplementedf io.BytesIO() p MyPickler(f) p.dump(MyClass)del MyClassunpickled_class pickle.loads(f.getvalue())assert isinstance(unpickled_class, type) assert unpickled_class.__name__ MyClass assert unpickled_class.my_attribute 1外部缓冲区 3.8 新版功能. 在某些场景中pickle 模块会被用来传输海量的数据。 因此最小化内存复制次数以保证性能和节省资源是很重要的。 但是 pickle 模块的正常运作会将图类对象结构转换为字节序列流因此在本质上就要从封存流中来回复制数据。 如果 provider (待传输对象类型的实现) 和 consumer (通信系统的实现) 都支持 pickle 第 5 版或更高版本所提供的外部传输功能则此约束可以被撤销。 提供方 API 大的待封存数据对象必须实现协议 5 及以上版本专属的 __reduce_ex__() 方法该方法将为任意大的数据返回一个 PickleBuffer 实例而不是 bytes 对象等。 PickleBuffer 对象会 表明 底层缓冲区可被用于外部数据传输。 那些对象仍将保持与 pickle 模块的正常用法兼容。 但是使用方也可以选择告知 pickle 它们将自行处理那些缓冲区。 使用方 API 当序列化一个对象图时通信系统可以启用对所生成 PickleBuffer 对象的定制处理。 发送端需要传递 buffer_callback 参数到 Pickler (或是到 dump() 或 dumps() 函数)该回调函数将在封存对象图时附带每个所生成的 PickleBuffer 被调用。 由 buffer_callback 所累积的缓冲区的数据将不会被拷贝到 pickle 流而是仅插入一个简单的标记。 接收端需要传递 buffers 参数到 Unpickler (或是到 load() 或 loads() 函数)其值是一个由缓冲区组成的可迭代对象它会被传递给 buffer_callback。 该可迭代对象应当按其被传递给 buffer_callback 时的顺序产生缓冲区。 这些缓冲区将提供对象重构造器所期望的数据对这些数据的封存产生了原本的 PickleBuffer 对象。 在发送端和接受端之间通信系统可以自由地实现它自己用于外部缓冲区的传输机制。 潜在的优化包括使用共享内存或基于特定数据类型的压缩等。 示例 下面是一个小例子在其中我们实现了一个 bytearray 的子类能够用于外部缓冲区封存: class ZeroCopyByteArray(bytearray):def __reduce_ex__(self, protocol):if protocol 5:return type(self)._reconstruct, (PickleBuffer(self),), Noneelse:# PickleBuffer is forbidden with pickle protocols 4.return type(self)._reconstruct, (bytearray(self),)classmethoddef _reconstruct(cls, obj):with memoryview(obj) as m:# Get a handle over the original buffer objectobj m.objif type(obj) is cls:# Original buffer object is a ZeroCopyByteArray, return it# as-is.return objelse:return cls(obj)重构造器 (_reconstruct 类方法) 会在缓冲区的提供对象具有正确类型时返回该对象。 在此小示例中这是模拟零拷贝行为的便捷方式。 在使用方我们可以按通常方式封存那些对象它们在反序列化时将提供原始对象的一个副本: b ZeroCopyByteArray(babc) data pickle.dumps(b, protocol5) new_b pickle.loads(data) print(b new_b) # True print(b is new_b) # False: a copy was made但是如果我们传入 buffer_callback 然后在反序列化时给回累积的缓冲区我们就能够取回原始对象: b ZeroCopyByteArray(babc) buffers [] data pickle.dumps(b, protocol5, buffer_callbackbuffers.append) new_b pickle.loads(data, buffersbuffers) print(b new_b) # True print(b is new_b) # True: no copy was made这个例子受限于 bytearray 会自行分配内存这一事实你无法基于另一个对象的内存创建 bytearray 的实例。 但是第三方数据类型例如 NumPy 数组则没有这种限制允许在单独进程或系统间传输时使用零拷贝的封存或是尽可能少地拷贝 。 参见 PEP 574 -- 带有外部数据缓冲区的 pickle 协议 5 限制全局变量 默认情况下解封将会导入在 pickle 数据中找到的任何类或函数。 对于许多应用来说此行为是不可接受的因为它会允许解封器导入并发起调用任意代码。 只须考虑当这个手工构建的 pickle 数据流被加载时会做什么: import picklepickle.loads(bcos\nsystem\n(Secho hello world\ntR.) hello world 0在这个例子里解封器导入 os.system() 函数然后应用字符串参数 echo hello world。 虽然这个例子不具攻击性但是不难想象别人能够通过此方式对你的系统造成损害。 出于这样的理由你可能会希望通过定制 Unpickler.find_class() 来控制要解封的对象。 与其名称所提示的不同Unpickler.find_class() 会在执行对任何全局对象例如一个类或一个函数的请求时被调用。 因此可以完全禁止全局对象或是将它们限制在一个安全的子集中。 下面的例子是一个解封器它只允许某一些安全的来自 builtins 模块的类被加载: import builtins import io import picklesafe_builtins {range,complex,set,frozenset,slice, }class RestrictedUnpickler(pickle.Unpickler):def find_class(self, module, name):# Only allow safe classes from builtins.if module builtins and name in safe_builtins:return getattr(builtins, name)# Forbid everything else.raise pickle.UnpicklingError(global %s.%s is forbidden %(module, name))def restricted_loads(s):Helper function analogous to pickle.loads().return RestrictedUnpickler(io.BytesIO(s)).load()我们这个解封器完成其功能的一个示例用法: restricted_loads(pickle.dumps([1, 2, range(15)])) [1, 2, range(0, 15)]restricted_loads(bcos\nsystem\n(Secho hello world\ntR.) Traceback (most recent call last):... pickle.UnpicklingError: global os.system is forbiddenrestricted_loads(bcbuiltins\neval\n ... b(S\getattr(__import__(os), system) ... b(echo hello world)\\ntR.) Traceback (most recent call last):... pickle.UnpicklingError: global builtins.eval is forbidden正如我们这个例子所显示的对于允许解封的对象你必须要保持谨慎。 因此如果要保证安全你可以考虑其他选择例如 xmlrpc.client 中的编组 API 或是第三方解决方案。 性能 较新版本的 pickle 协议第 2 版或更高具有针对某些常见特性和内置类型的高效二进制编码格式。 此外pickle 模块还拥有一个以 C 编写的透明优化器。 例子 对于最简单的代码请使用 dump() 和 load() 函数。 import pickle# An arbitrary collection of objects supported by pickle. data {a: [1, 2.0, 34j],b: (character string, bbyte string),c: {None, True, False} }with open(data.pickle, wb) as f:# Pickle the data dictionary using the highest protocol available.pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)以下示例读取之前封存的数据。 import picklewith open(data.pickle, rb) as f:# The protocol version used is detected automatically, so we do not# have to specify it.data pickle.load(f)参见 模块 copyreg 为扩展类型提供 pickle 接口所需的构造函数。 模块 pickletools 用于处理和分析已封存数据的工具。 模块 shelve 带索引的数据库用于存放对象使用了 pickle 模块。 模块 copy 浅层 (shallow) 和深层 (deep) 复制对象操作 模块 marshal 高效地序列化内置类型的数据。 备注 1 不要把它与 marshal 模块混淆。 2 这就是为什么 lambda 函数不可以被封存所有的匿名函数都有同一个名字lambda。 3 抛出的异常有可能是 ImportError 或 AttributeError也可能是其他异常。 4 copy 模块使用这一协议实现浅层 (shallow) 和深层 (deep) 复制操作。 5 对于字符数字类字符的限制是由于持久化 ID 在协议版本 0 中是由分行符来分隔的。 因此如果持久化 ID 中出现了任何形式的分行符封存结果就将变得无法读取。
http://www.yutouwan.com/news/240018/

相关文章:

  • 网站的动态新闻数据库怎么做化妆品网站设计报告
  • 深圳网站设计go平顶山市城乡建设职工培训网站
  • 婚纱摄影网站html模板网站开发为什么采取ssh框架
  • flash制作动画教程软件开发网站能做seo吗
  • 网站空间代理网页制作与开发教程
  • 福州高端网站建设关于网站建设的请示报告
  • 门户网站开发需要多少钱网站如何做搜索
  • 法律推广网站公司网站模块制作
  • qq业务网站平台广州免费律师咨询
  • 做一个静态网站导航要多少钱最超值的锦州网站建设
  • 网站开发 搜索网站建站费用多少
  • 网站群发推广软件一键优化助手
  • 网站制作眼企业网站建设版本
  • wordpress 建企业网站网站跳出率多少算正常
  • 会用框架做网站能找到工作吗wordpress 重置密码链接
  • 电商网站建设文献aws wordpress cdn
  • 500云空间网站域名查询站长工具
  • 教育培训网站模板资源下载类网站源码
  • 建设部网站官网办事厅网上商城建设方案
  • 教育培训行业网站建设免费下载百度并安装
  • 学校ftp服务器做网站亚马逊平台的运营模式
  • 建设银行的网站是多少钱企业网站建设可以分为几个层次
  • 网站建设 品牌塑造计划wordpress新建关于我们页面
  • 个人门户网站开发网站如何做质保系统
  • qq代刷网站推广免费广州软件公司排名
  • 免费网站建设解决方案网络舆情现状分析
  • 旅游网站毕业设计和论文国际新闻最新消息今天200字
  • 南京服装网站建设wordpress 发布api
  • rails 开发的网站开发企业网站建设的思路
  • 福安市网站建设hexo wordpress 主题