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

Python 增强提案

PEP 455 – 为 collections 模块添加键变换字典

作者:
Antoine Pitrou <solipsis at pitrou.net>
BDFL-代表:
Raymond Hettinger
状态:
已拒绝
类型:
标准跟踪
创建时间:
2013-09-13
Python 版本:
3.5
历史记录:


目录

摘要

本 PEP 针对 collections 模块提出了一种新的数据结构,在本 PEP 中称为“TransformDict”。该结构是一种可变映射,在进行查找时使用给定函数转换键,但在读取时保留原始键。

拒绝

请参阅 https://mail.python.org/pipermail/python-dev/2015-May/140003.html 的基本原理,以及 https://mail.python.org/pipermail/python-dev/2013-October/129937.html 的早期部分审查。

基本原理

这种模式存在许多专门的版本。最常见的是不区分大小写、保留大小写的字典,即类似字典的容器,它以不区分大小写的方式匹配键,但保留原始大小写。在网络编程中,这是一个非常常见的需求,因为许多协议在其消息中包含一些“键/值”属性数组,其中键是文本字符串,其大小写指定为在接收时忽略,但根据规范或自定义在重新传输时应保留或进行非平凡的规范化。

另一个常见的请求是标识字典,其中键根据其各自的 id() 而不是正常匹配进行匹配。

两者都是更一般模式的实例,其中在查找键时会应用给定的转换函数:该函数在第一个示例中为 str.lowerstr.casefold,在第二个示例中为内置的 id 函数。

(可以说,该模式将键投影从用户可见集到内部查找集。)

语义

TransformDict 是一个 MutableMapping 实现:它忠实地实现了可变映射的众所周知的 API,例如 dict 本身以及标准库中的其他类似字典的类。因此,本 PEP 不会重复大多数 TransformDict 方法的语义。

转换函数不必是双射的,它可以像不区分大小写的示例一样严格地满射(换句话说,不同的键可以查找相同的值)

>>> d = TransformDict(str.casefold)
>>> d['SomeKey'] = 5
>>> d['somekey']
5
>>> d['SOMEKEY']
5

TransformDict 保留创建条目时使用的第一个键

>>> d = TransformDict(str.casefold)
>>> d['SomeKey'] = 1
>>> d['somekey'] = 2
>>> list(d.items())
[('SomeKey', 2)]

原始键不必是可散列的,只要转换函数返回可散列的键即可

>>> d = TransformDict(id)
>>> l = [None]
>>> d[l] = 5
>>> l in d
True

构造函数

如上面的示例所示,创建 TransformDict 需要将键转换函数作为第一个参数传递(就像创建 defaultdict 需要将工厂函数作为第一个参数传递一样)。

构造函数还接受其他可选参数,这些参数可用于使用某些键值对初始化 TransformDict。这些可选参数与 dictdefaultdict 构造函数中的参数相同

>>> d = TransformDict(str.casefold, [('Foo', 1)], Bar=2)
>>> sorted(d.items())
[('Bar', 2), ('Foo', 1)]

获取原始键

TransformDict 还具有一个查找方法,该方法返回存储的键以及相应的价值

>>> d = TransformDict(str.casefold, {'Foo': 1})
>>> d.getitem('FOO')
('Foo', 1)
>>> d.getitem('bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'bar'

方法名称 getitem() 遵循可变映射上的标准 popitem() 方法。

获取转换函数

TransformDict 具有一个简单的只读属性 transform_func,它返回转换函数。

备选提案和问题

保留最后一个原始键

大多数 python-dev 响应者发现保留第一个用户提供的键比保留最后一个更直观。此外,它与使用不同但相等键时的 dict 对象自己的行为相匹配

>>> d = {}
>>> d[1] = 'hello'
>>> d[1.0] = 'world'
>>> d
{1: 'world'}

此外,在第一个键保留方案中显式保留最后一个键仍然可以使用以下方法

d.pop(key, None)
d[key] = value

而相反(在最后一个键保留方案中保留第一个键)在不重写容器部分代码的情况下似乎不可能。

使用编码器/解码器对

使用函数对不是必需的,因为容器保留了原始键。此外,编码器/解码器对将要求转换是双射的,这会阻止诸如不区分大小写匹配之类的重要用例。

为值提供转换函数

字典值不用于查找,其语义与容器的操作完全无关。因此,没有必要同时拥有“原始”值和“转换”值:转换值不会用于任何用途。

提供专用容器,而不是通用容器

有人问为什么我们提供通用的 TransformDict 构造而不是专门的不区分大小写的字典变体。答案是提供通用构造几乎与专门构造一样便宜(在代码和性能方面),并且它可以满足更多用例。

即使是不区分大小写的字典实际上也可以引出不同的转换函数:str.lowerstr.casefold 或在某些情况下与 ASCII 兼容编码的文本一起使用时使用 bytes.lower

其他构造函数模式

Serhiy Storchaka 提出了另外两种构造函数模式

  • 类型工厂方案
    d = TransformDict(str.casefold)(Foo=1)
    
  • 子类化方案
    class CaseInsensitiveDict(TransformDict):
        __transform__ = str.casefold
    
    d = CaseInsensitiveDict(Foo=1)
    

虽然这两种方法都可以被证明是正确的,但它们不符合标准库中已建立的实践,因此被拒绝。

实现

针对 collections 模块的补丁在错误跟踪器上跟踪:http://bugs.python.org/issue18986.

现有工作

不区分大小写的字典是一个流行的请求

标识字典也已提出请求

标准库中的几个模块使用标识查找来进行对象记忆,例如 picklejsoncopycProfiledoctest_threading_local

其他语言

C# / .Net

.Net 具有一个通用的 Dictionary 类,您可以在其中指定自定义 IEqualityComparerhttp://msdn.microsoft.com/en-us/library/xfhwa508.aspx

使用它是编写不区分大小写字典的推荐方法:http://stackoverflow.com/questions/13230414/case-insensitive-access-for-generic-dictionary

Java

Java 具有一个专门的 CaseInsensitiveMaphttp://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html

它还具有一个单独的 IdentityHashMaphttp://docs.oracle.com/javase/6/docs/api/java/util/IdentityHashMap.html

C++

C++ 标准模板库具有一个具有可自定义散列和相等函数的 unordered_maphttp://www.cplusplus.com/reference/unordered_map/unordered_map/


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

最后修改时间:2023-09-09 17:39:29 GMT