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.lower
或 str.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。这些可选参数与 dict
和 defaultdict
构造函数中的参数相同
>>> 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.lower
、str.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.
现有工作
不区分大小写的字典是一个流行的请求
- http://twistedmatrix.com/documents/current/api/twisted.python.util.InsensitiveDict.html
- https://mail.python.org/pipermail/python-list/2013-May/647243.html
- https://mail.python.org/pipermail/python-list/2005-April/296208.html
- https://mail.python.org/pipermail/python-list/2004-June/241748.html
- http://bugs.python.org/msg197376
- http://stackoverflow.com/a/2082169
- http://stackoverflow.com/a/3296782
- http://code.activestate.com/recipes/66315-case-insensitive-dictionary/
- https://gist.github.com/babakness/3901174
- http://www.wikier.org/blog/key-insensitive-dictionary-in-python
- http://en.sharejs.com/python/14534
- http://www.voidspace.org.uk/python/archive.shtml#caseless
标识字典也已提出请求
- https://mail.python.org/pipermail/python-ideas/2010-May/007235.html
- http://www.gossamer-threads.com/lists/python/python/209527
标准库中的几个模块使用标识查找来进行对象记忆,例如 pickle
、json
、copy
、cProfile
、doctest
和 _threading_local
。
其他语言
C# / .Net
.Net 具有一个通用的 Dictionary
类,您可以在其中指定自定义 IEqualityComparer
:http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
使用它是编写不区分大小写字典的推荐方法:http://stackoverflow.com/questions/13230414/case-insensitive-access-for-generic-dictionary
Java
Java 具有一个专门的 CaseInsensitiveMap
:http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
它还具有一个单独的 IdentityHashMap
:http://docs.oracle.com/javase/6/docs/api/java/util/IdentityHashMap.html
C++
C++ 标准模板库具有一个具有可自定义散列和相等函数的 unordered_map
:http://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