冻结集就是冻结集。 冻结列表可以是元组。 冷冻字典会是什么?一个不可变的、可哈希的字典。

我猜它可能是collections.namedtuple之类的东西,但那更像是一个冻结的字典(一个半冻结的字典)。不是吗?

一个“frozendict”应该是一个冻结的字典,它应该有键,值,get等,并支持in, for等。

更新: 在这里:https://www.python.org/dev/peps/pep-0603


当前回答

奇怪的是,尽管我们有很少有用的frozenset,但仍然没有冻结映射。这个想法在PEP 416中被否决了——添加一个frozendict内置类型。这个想法可能会在后面的Python版本中重新讨论,请参阅PEP 603—向集合添加frozenmap类型。

所以Python 2的解决方案是:

def foo(config={'a': 1}):
    ...

这似乎仍是常态:

def foo(config=None):
    if config is None:
        config = {'a': 1}  # default config
    ...

在Python 3中,你可以这样做:

from types import MappingProxyType

default_config = {'a': 1}
DEFAULTS = MappingProxyType(default_config)

def foo(config=DEFAULTS):
    ...

现在,默认配置可以动态更新,但在您希望它不可变的地方保持不变,只需传递代理即可。

因此,default_config中的更改将按预期更新默认值,但不能写入映射代理对象本身。

诚然,它与“不可变的、可哈希的字典”不是一回事,但它可能是frozendict某些用例的体面替代品。

其他回答

是的,这是我的第二个答案,但这是一个完全不同的方法。第一个实现是用纯python实现的。这是Cython的。如果您知道如何使用和编译Cython模块,这与使用常规字典一样快。大约0.04到0.06微秒来检索一个值。

这是frozen_dict。pyx文件

import cython
from collections import Mapping

cdef class dict_wrapper:
    cdef object d
    cdef int h

    def __init__(self, *args, **kw):
        self.d = dict(*args, **kw)
        self.h = -1

    def __len__(self):
        return len(self.d)

    def __iter__(self):
        return iter(self.d)

    def __getitem__(self, key):
        return self.d[key]

    def __hash__(self):
        if self.h == -1:
            self.h = hash(frozenset(self.d.iteritems()))
        return self.h

class FrozenDict(dict_wrapper, Mapping):
    def __repr__(self):
        c = type(self).__name__
        r = ', '.join('%r: %r' % (k,self[k]) for k in self)
        return '%s({%s})' % (c, r)

__all__ = ['FrozenDict']

这是setup。py文件

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize('frozen_dict.pyx')
)

如果安装了Cython,请将上述两个文件保存到同一目录中。在命令行中移动到该目录。

python setup.py build_ext --inplace
python setup.py install

你应该做完了。

我需要在某一时刻访问某个固定键用于某种全局常量类型的东西我确定了这样的东西:

class MyFrozenDict:
    def __getitem__(self, key):
        if key == 'mykey1':
            return 0
        if key == 'mykey2':
            return "another value"
        raise KeyError(key)

像这样使用它

a = MyFrozenDict()
print(a['mykey1'])

警告:对于大多数用例,我不建议这样做,因为它需要进行一些相当严重的权衡。

奇怪的是,尽管我们有很少有用的frozenset,但仍然没有冻结映射。这个想法在PEP 416中被否决了——添加一个frozendict内置类型。这个想法可能会在后面的Python版本中重新讨论,请参阅PEP 603—向集合添加frozenmap类型。

所以Python 2的解决方案是:

def foo(config={'a': 1}):
    ...

这似乎仍是常态:

def foo(config=None):
    if config is None:
        config = {'a': 1}  # default config
    ...

在Python 3中,你可以这样做:

from types import MappingProxyType

default_config = {'a': 1}
DEFAULTS = MappingProxyType(default_config)

def foo(config=DEFAULTS):
    ...

现在,默认配置可以动态更新,但在您希望它不可变的地方保持不变,只需传递代理即可。

因此,default_config中的更改将按预期更新默认值,但不能写入映射代理对象本身。

诚然,它与“不可变的、可哈希的字典”不是一回事,但它可能是frozendict某些用例的体面替代品。

Python没有内置的frozendict类型。事实证明,这并不经常有用(尽管它仍然可能比frozenset更有用)。

需要这种类型的最常见原因是在记忆函数调用带有未知参数的函数时。存储dict(其中值是可哈希的)的可哈希等价对象的最常见解决方案是类似tuple(sorted(kwargs.items()))的东西。

这取决于排序是不是有点疯狂。Python不能肯定地保证排序会产生合理的结果。(但它不能承诺太多其他东西,所以不要太担心。)


你可以很容易地做一些类似字典的包装。它可能看起来像

import collections

class FrozenDict(collections.Mapping):
    """Don't forget the docstrings!!"""
    
    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)
        self._hash = None

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    def __getitem__(self, key):
        return self._d[key]

    def __hash__(self):
        # It would have been simpler and maybe more obvious to 
        # use hash(tuple(sorted(self._d.iteritems()))) from this discussion
        # so far, but this solution is O(n). I don't know what kind of 
        # n we are going to run into, but sometimes it's hard to resist the 
        # urge to optimize when it will gain improved algorithmic performance.
        if self._hash is None:
            hash_ = 0
            for pair in self.items():
                hash_ ^= hash(pair)
            self._hash = hash_
        return self._hash

它应该工作得很好:

>>> x = FrozenDict(a=1, b=2)
>>> y = FrozenDict(a=1, b=2)
>>> x is y
False
>>> x == y
True
>>> x == {'a': 1, 'b': 2}
True
>>> d = {x: 'foo'}
>>> d[y]
'foo'

在没有本地语言支持的情况下,您可以自己动手,也可以使用现有的解决方案。幸运的是,Python使得扩展它们的基本实现变得非常简单。

class frozen_dict(dict):
    def __setitem__(self, key, value):
        raise Exception('Frozen dictionaries cannot be mutated')

frozen_dict = frozen_dict({'foo': 'FOO' })
print(frozen['foo']) # FOO
frozen['foo'] = 'NEWFOO' # Exception: Frozen dictionaries cannot be mutated

# OR

from types import MappingProxyType

frozen_dict = MappingProxyType({'foo': 'FOO'})
print(frozen_dict['foo']) # FOO
frozen_dict['foo'] = 'NEWFOO' # TypeError: 'mappingproxy' object does not support item assignment