有没有一种类似字典的对象是不可改变的?[重复]

问题描述 投票:0回答:1

我想要一个Python对象,它可以灵活地采用任何键,我可以通过键来访问,就像一个字典,但却是不可改变的。一种选择是灵活地生成一个 namedtuple 但这样做是不是不好?在下面的例子中,一个linter人不会期望得到 nt 有属性 a 比如说。

例子:我想要一个Python对象,它可以灵活地取任何键,我可以通过键来访问,就像字典一样,但不可改变。

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})
python collections namedtuple
1个回答
2
投票

我在评论中提到,我不确定为什么需要这样做。但我们可以简单地覆盖 __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)

这将产生 {'a': 1, 'b': 2} 从而忽略了 x['c'] = 3 套。


些好处

与命名的tuple相比,使用字典继承的速度差在40-1000倍之间。(参见下面的粗略速度测试)

in 操作符可以在字典上使用,但在命名的元组上就不是那么好用了。

'a' in nt == False
'a' in x == True

你可以用键访问字典的方式来代替 恰如其分 JavaScript风格

x['a'] == nt.a

虽然这是个口味问题。 你也不必挑剔键,因为字典基本上支持任何键标识符。

x[1] = 'a number'
nt = foo({1 : 'a number'})

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


优化 (定时的事情)

现在,这只是一个粗略的例子,它会有很多不同,取决于系统,月亮在天空中的位置等等。但作为一个粗略的例子。

import time
from collections import namedtuple

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

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

    def __setitem__(self, key, val):
        pass

    def __type__(self, *args, **kwargs):
        return dict

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

start = time.time()
for i in range(1000000):
    nt = foo({'x'+str(i) : i})
end = time.time()
print('Named tuples:', end - start,'seconds.')

start = time.time()
for i in range(1000000):
    x = autodict({'x'+str(i) : i})
end = time.time()
print('Autodict:', end - start,'seconds.')

结果是:

Named tuples: 59.21987843513489 seconds.
Autodict: 1.4844810962677002 seconds.

在我看来,字典的设置是非常快的。虽然这很可能与多 for 循环在命名元组设置中,这也许可以很容易地得到一些补救。但对于基本的理解来说,这是一个很大的区别。这个例子显然没有测试较大的一次性创建或访问时间。只是,"如果你在一段时间内使用这些选项来创建数据集,你会损失多少时间":)

奖励:如果你有一个大的基础字典,并想冻结它?

base_dict = {'x'+str(i) : i for i in range(1000000)}

start = time.time()
nt = foo(base_dict)
end = time.time()
print('Named tuples:', end - start,'seconds.')

start = time.time()
x = autodict(base_dict)
end = time.time()
print('Autodict:', end - start,'seconds.')

嗯,差异比我想象的要大。x1038.5 倍的速度。(我用CPU做其他事情,但我认为这是公平的游戏)

Named tuples: 154.0662612915039 seconds.
Autodict: 0.1483476161956787 seconds.

1
投票

你可以使用 frozenset() 来存储数据,然后添加一个自定义的 __getitem__() 方法。

class Idict:
    def __init__(self, d):
        self.d = frozenset(d.items())

    def __getitem__(self, k):
        return [v for _k,v in self.d if _k == k][0]


d = {'a':1, 'b':2}
a = Idict(d)
a['a'] #1
a['h'] = 0 #TypeError: 'Idict' object does not support item assignment
© www.soinside.com 2019 - 2024. All rights reserved.