是否有一个类似字典的不可变对象?

Jam*_*mbe 3 python collections namedtuple

我想要一个Python对象,它可以灵活地接受任何键,并且我可以通过键访问,就像字典一样,但它是不可变的。一种选择可能是灵活地生成 anamedtuple但这样做是不好的做法吗?在下面的示例中,linter 不会期望nt具有属性a

例子:

from collections import namedtuple

def foo(bar):
    MyNamedTuple = namedtuple("MyNamedTuple", [k for k in bar.keys()])
    d = {k: v for k, v in bar.items()}
    return MyNamedTuple(**d)

>>> nt = foo({"a": 1, "b": 2})
Run Code Online (Sandbox Code Playgroud)

Tor*_*xed 6

我在评论中提到过,我不确定为什么需要这样做。
但我们可以简单地重写字典__setitem__类。尽管如此,这可能(很可能)会导致问题。一个最小的例子是:

class autodict(dict):
    def __init__(self, *args, **kwargs):
        super(autodict, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        return val

    def __setitem__(self, key, val):
        pass

x = autodict({'a' : 1, 'b' : 2})
x['c'] = 3
print(x)
Run Code Online (Sandbox Code Playgroud)

这将产生{'a': 1, 'b': 2}并因此忽略该x['c'] = 3集合。


一些好处

与命名元组相比,使用字典继承的速度快了 40-1000 倍。(粗速度测试见下文)

in运算符适用于字典,但在命名元组上效果不佳,如下所示:

'a' in nt == False
'a' in x == True
Run Code Online (Sandbox Code Playgroud)

您可以使用键访问字典样式代替(由于缺乏更好的术语) JavaScript 样式

x['a'] == nt.a
Run Code Online (Sandbox Code Playgroud)

虽然这是一个品味问题。

您也不必对键挑剔,因为字典基本上支持任何键标识符:

x[1] = 'a number'
nt = foo({1 : 'a number'})
Run Code Online (Sandbox Code Playgroud)

命名元组将导致Type names and field names must be valid identifiers: '1'


优化(时间安排)

现在,这是一个粗略的例子,它会根据系统、月亮在天空中的位置等而有很大差异。但作为一个粗略的例子:

class autodict(dict):
    def __init__(self, *args, **kwargs):
        super(autodict, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        return val

    def __setitem__(self, key, val):
        pass

x = autodict({'a' : 1, 'b' : 2})
x['c'] = 3
print(x)
Run Code Online (Sandbox Code Playgroud)

结果是:

Named tuples: 59.21987843513489 seconds.
Autodict: 1.4844810962677002 seconds.
Run Code Online (Sandbox Code Playgroud)

字典设置在我的书中,速度快得惊人。尽管这很可能与命名元组设置中的多个循环有关for,并且可以通过某种方式轻松修复。但对于基本理解来说,这是一个很大的差异。该示例显然没有测试更大的一次性创建或访问时间。只是,“如果您使用这些选项在一段时间内创建数据集,您会损失多少时间”:)

额外奖励:如果您有一个很大的基础字典并且想要冻结它怎么办?

'a' in nt == False
'a' in x == True
Run Code Online (Sandbox Code Playgroud)

嗯,差异比我预期的要大……x1038.5快几倍。
(我将 CPU 用于其他用途,但我认为这是公平的游戏)

Named tuples: 154.0662612915039 seconds.
Autodict: 0.1483476161956787 seconds.
Run Code Online (Sandbox Code Playgroud)