Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python增强提案

PEP 416 – 添加frozendict内置类型

作者:
Victor Stinner <vstinner at python.org>
状态:
已拒绝
类型:
标准跟踪
创建:
2012年2月29日
Python版本:
3.3

目录

拒绝通知

我拒绝了这个PEP。一些原因(并非详尽):

  • 根据Raymond Hettinger的说法,frozendict的使用率很低。那些使用它的人往往只是将其作为提示,例如声明全局或类级别的“常量”:它们并非真正不可变,因为任何人都可以对名称进行赋值。
  • 存在避免可变默认值的习惯用法。
  • 在PyPy中使用frozendict优化代码的潜力尚不确定;首先需要更改很多其他内容。编译时查找通常也是如此。
  • 多个线程可以通过约定同意不修改共享字典,没有必要强制执行。多个进程无法共享字典。
  • 添加用Python编写的安全沙箱,即使范围有限,也遭到许多人的反对,因为天生难以证明沙箱实际上是安全的。因此,我们不会很快将其添加到标准库中,因此此用例超出了PEP的范围。

另一方面,将现有的只读字典代理公开为内置类型对我来说听起来不错。(需要对其进行更改以允许调用构造函数。)GvR。

更新(2012-04-15):一个新的MappingProxyType类型已添加到Python 3.3的types模块中。

摘要

添加一个新的frozendict内置类型。

基本原理

frozendict是一个只读映射:无法添加或删除键,并且键始终映射到相同的值。但是,frozendict的值可以不可哈希。frozendict 可哈希当且仅当所有值都可哈希。

用例

  • 像默认配置这样的不可变全局变量。
  • 函数参数的默认值。避免可变默认参数的问题。
  • 实现缓存:frozendict可用于存储函数关键字。frozendict可用于映射的键或集合的成员。
  • 当frozendict由多个线程或进程共享时,frozendict避免了锁的需求,尤其是可哈希的frozendict。它还有助于禁止协程(生成器+greenlets)修改全局状态。
  • frozendict查找可以在编译时而不是运行时完成,因为映射是只读的。frozendict可以用来代替预处理器在编译时移除条件代码,例如特定于调试版本的代码。
  • frozendict有助于为安全模块实现只读对象代理。例如,可以将frozendict类型用于__builtins__映射或type.__dict__。这是可能的,因为frozendict与PyDict C API兼容。
  • frozendict在某些情况下避免了对只读代理的需求。frozendict比代理快,因为获取frozendict中的项是快速查找,而代理需要函数调用。

约束

  • frozendict必须实现Mapping抽象基类
  • frozendict键和值可以是不可排序的
  • 如果所有键和值都可哈希,则frozendict可哈希
  • frozendict哈希不依赖于项的创建顺序

实现

  • 添加一个基于PyDictObject的PyFrozenDictObject结构,并带有一个额外的“Py_hash_t hash;”字段
  • frozendict.__hash__()使用hash(frozenset(self.items()))实现,并在其私有hash属性中缓存结果
  • 将frozendict注册为collections.abc.Mapping
  • frozendict可与PyDict_GetItem()一起使用,但PyDict_SetItem()和PyDict_DelItem()会引发TypeError

方案:可哈希字典

为了确保frozendict可哈希,可以在创建frozendict之前检查值

import itertools

def hashabledict(*args, **kw):
    # ensure that all values are hashable
    for key, value in itertools.chain(args, kw.items()):
        if isinstance(value, (int, str, bytes, float, frozenset, complex)):
            # avoid the compute the hash (which may be slow) for builtin
            # types known to be hashable for any value
            continue
        hash(value)
        # don't check the key: frozendict already checks the key
    return frozendict.__new__(cls, *args, **kw)

反对意见

namedtuple可能符合frozendict的要求。

namedtuple不是映射,它没有实现Mapping抽象基类。

frozendict可以使用描述符”和“frozendict只需要在实践中是常量”来实现。

如果frozendict用于强化Python(安全目的),则必须用C实现。用C实现的类型也更快。

PEP 351已被拒绝。

PEP 351试图冻结一个对象,因此可能会将一个可变对象转换为一个不可变对象(使用不同的类型)。frozendict不会转换任何内容:如果值不可哈希,则hash(frozendict)会引发TypeError。冻结对象不是这个PEP的目的。

替代方案:dictproxy

Python有一个内置的dictproxy类型,由type.__dict__ getter描述符使用。此类型不公开。dictproxy是字典的只读视图,但它不是只读映射。如果修改了字典,则dictproxy也会被修改。

可以使用ctypes和Python C API使用dictproxy,例如,请参阅Ikkei Shimomura的通过ctypes.pythonapi和type()创建dictproxy对象(Python配方576540)。该配方包含一个测试,检查dictproxy是否“可变”(修改与dictproxy链接的字典)。

但是,dictproxy在某些情况下可能很有用,在这些情况下,其可变属性不是问题,可以避免复制字典。

现有实现

白名单方法。

  • 实现不可变字典(Python配方498072)作者:Aristotelis Mikropoulos。类似于frozendict,只是它不是真正只读的:可以访问此私有内部字典。它没有实现__hash__,并且存在实现问题:可以再次调用__init__()来修改映射。
  • PyWebmail包含一个ImmutableDict类型:webmail.utils.ImmutableDict。如果键和值可哈希,则它可哈希。它不是真正只读的:其内部字典是公共属性。
  • remember项目:remember.dicts.FrozenDict。它用于实现缓存:FrozenDict用于存储函数回调。FrozenDict可能是可哈希的。它有一个额外的supply_dict()类方法,用于从字典创建FrozenDict而不复制字典:将字典存储为内部字典。实现问题:可以调用__init__()来修改映射,并且哈希可能因项创建顺序而异。映射不是真正只读的:内部字典可在Python中访问。

黑名单方法:继承自dict并覆盖写入方法以引发异常。它不是真正只读的:仍然可以在此类“冻结字典”上调用字典方法来修改它。

可哈希字典:继承自dict并只添加__hash__方法。


来源:https://github.com/python/peps/blob/main/peps/pep-0416.rst

上次修改:2023-09-09 17:39:29 GMT