Một dictal đông lạnh là gì?


158
  • Một bộ đông lạnh là một froundredet.
  • Một danh sách đông lạnh có thể là một tuple.
  • Một dict đông lạnh sẽ là gì? Một dict bất biến, băm.

Tôi đoán nó có thể là một cái gì đó giống như collections.namedtuple, nhưng nó giống như một mệnh lệnh khóa đông lạnh (một chính tả nửa đông lạnh). Phải không?

Một "frozendict" nên được một cuốn từ điển đông lạnh, nó nên có keys, values, get, vv, và hỗ trợ in, forvv

cập nhật:
* đây là: https://www.python.org/dev/peps/pep-0603

Câu trả lời:


120

Python không có kiểu frozendict dựng sẵn. Hóa ra điều này sẽ không hữu ích quá thường xuyên (mặc dù nó có thể vẫn hữu ích hơn thường xuyên frozenset).

Lý do phổ biến nhất để muốn loại như vậy là khi ghi nhớ hàm gọi cho các hàm có đối số không xác định. Giải pháp phổ biến nhất để lưu trữ tương đương có thể băm của một dict (trong đó các giá trị có thể băm) là một cái gì đó như thế tuple(sorted(kwargs.iteritems())).

Điều này phụ thuộc vào việc sắp xếp không một chút điên rồ. Python không thể tích cực hứa hẹn sắp xếp sẽ dẫn đến một cái gì đó hợp lý ở đây. (Nhưng nó không thể hứa hẹn nhiều thứ khác, vì vậy đừng đổ mồ hôi quá nhiều.)


Bạn có thể dễ dàng tạo ra một số loại trình bao bọc hoạt động giống như một lệnh. Nó có thể trông giống như

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

Nó nên hoạt động tuyệt vời:

>>> 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'

7
Tôi không biết mức độ an toàn của luồng mà mọi người lo lắng với loại điều này, nhưng về mặt đó __hash__phương pháp của bạn có thể được cải thiện đôi chút. Chỉ cần sử dụng một biến tạm thời khi tính toán hàm băm và chỉ được đặt self._hashkhi bạn có giá trị cuối cùng. Bằng cách đó, một luồng khác nhận được hàm băm trong khi đầu tiên là tính toán sẽ chỉ đơn giản là thực hiện tính toán dự phòng, thay vì nhận một giá trị không chính xác.
Jeff DQ

22
@Jeff Theo quy định, tất cả các mã ở mọi nơi đều không an toàn cho luồng và bạn nên bọc nó xung quanh một số cấu trúc đồng bộ hóa để sử dụng mã đó một cách an toàn. Ngoài ra, khái niệm cụ thể của bạn về an toàn luồng cũng phụ thuộc vào tính nguyên tử của việc gán thuộc tính đối tượng, điều này không được đảm bảo.
Devin Jeanpierre

9
@Anentropic, Điều đó không đúng chút nào.
Mike Graham

17
Được cảnh báo: "FrozenDict" này không nhất thiết phải đóng băng. Không có gì ngăn bạn đặt một danh sách có thể thay đổi làm giá trị, trong trường hợp đó băm sẽ gây ra lỗi. Không có gì sai với điều đó, nhưng người dùng nên lưu ý. Một điều nữa: Thuật toán băm này được lựa chọn kém, rất dễ bị va chạm băm. Ví dụ: {'a': 'b'} băm giống như {'b': 'a'} và {'a': 1, 'b': 2} băm giống như {'a': 2, ' b ': 1}. Lựa chọn tốt hơn sẽ là self._hash ^ = hash ((khóa, giá trị))
Steve Byrnes

6
Nếu bạn thêm một mục có thể thay đổi trong một đối tượng bất biến, hai hành vi có thể xảy ra là ném lỗi khi tạo đối tượng hoặc ném lỗi khi băm đối tượng. Tuples làm cái sau, froundredet làm cái trước. Tôi chắc chắn nghĩ rằng bạn đã có một quyết định tốt để thực hiện phương pháp sau, tất cả mọi thứ được xem xét. Tuy nhiên, tôi nghĩ rằng mọi người có thể thấy rằng FrozenDict và froundredet có cùng tên, và đưa ra kết luận rằng họ nên cư xử tương tự nhau. Vì vậy, tôi nghĩ rằng nó đáng để cảnh báo mọi người về sự khác biệt này. :-)
Steve Byrnes

62

Thật kỳ lạ, mặc dù chúng tôi hiếm khi hữu ích frozensettrong trăn, nhưng vẫn không có ánh xạ đóng băng. Ý tưởng đã bị từ chối trong PEP 416 - Thêm loại dựng sẵn frozendict . Ý tưởng có thể được xem xét lại trong Python 3.9, xem PEP 603 - Thêm loại đông lạnh vào bộ sưu tập .

Vì vậy, giải pháp python 2 cho điều này:

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

Vẫn có vẻ hơi khập khiễng:

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

Trong python3, bạn có tùy chọn này :

from types import MappingProxyType

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

def foo(config=DEFAULTS):
    ...

Bây giờ cấu hình mặc định có thể được cập nhật động, nhưng vẫn không thay đổi khi bạn muốn nó bất biến bằng cách chuyển qua proxy thay thế.

Vì vậy, những thay đổi trong default_configbản cập nhật sẽ được cập nhật DEFAULTSnhư mong đợi, nhưng bạn không thể ghi vào chính đối tượng proxy ánh xạ.

Phải thừa nhận rằng nó không hoàn toàn giống như một "chính tả bất khả xâm phạm, có thể băm" - nhưng đó là một sự thay thế hợp lý cho cùng một loại trường hợp sử dụng mà chúng ta có thể muốn có một frozendict.


2
Có bất kỳ lý do cụ thể để lưu trữ proxy trong một biến mô-đun? Tại sao không chỉ def foo(config=MappingProxyType({'a': 1})):? Ví dụ của bạn vẫn cho phép sửa đổi toàn cầu thông qua default_config, quá.
jpmc26

Ngoài ra, tôi nghi ngờ việc gán đôi trong config = default_config = {'a': 1}là một lỗi đánh máy.
jpmc26

21

Giả sử các khóa và giá trị của từ điển là bất biến (ví dụ: chuỗi) thì:

>>> d
{'forever': 'atones', 'minks': 'cards', 'overhands': 'warranted', 
 'hardhearted': 'tartly', 'gradations': 'snorkeled'}
>>> t = tuple((k, d[k]) for k in sorted(d.keys()))
>>> hash(t)
1524953596

Đây là một đại diện tốt, kinh điển, bất biến của một chế độ (cấm hành vi so sánh điên rồ làm rối tung các loại).
Mike Graham

6
@devin: đồng ý đầy đủ, nhưng tôi sẽ để bài đăng của mình là một ví dụ cho thấy thường có cách tốt hơn nữa.
msw

14
Thậm chí tốt hơn là đặt nó trong một froundredet, không yêu cầu các khóa hoặc giá trị để có một thứ tự nhất quán được xác định.
asmeker

7
Chỉ có một vấn đề với điều này: bạn không còn có bản đồ. Đó sẽ là toàn bộ quan điểm của việc có chính tả đóng băng ở nơi đầu tiên.
Nhà vật lý điên

2
Phương pháp này thực sự tốt đẹp khi quay trở lại một dict. chỉ đơn giảndict(t)
codythecoder

12

Không có fronzedict, nhưng bạn có thể sử dụng MappingProxyTypeđã được thêm vào thư viện chuẩn với Python 3.3:

>>> from types import MappingProxyType
>>> foo = MappingProxyType({'a': 1})
>>> foo
mappingproxy({'a': 1})
>>> foo['a'] = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> foo
mappingproxy({'a': 1})

với lời cảnh báo:TypeError: can't pickle mappingproxy objects
Radu

Tôi thích ý tưởng này. Tôi sẽ thử nó.
Doug

10

Đây là mã tôi đã sử dụng. Tôi phân lớp froundredet. Những lợi thế của điều này là sau đây.

  1. Đây là một đối tượng thực sự bất biến. Không dựa vào hành vi tốt của người dùng và nhà phát triển trong tương lai.
  2. Thật dễ dàng để chuyển đổi qua lại giữa một từ điển thông thường và một từ điển đông lạnh. FrozenDict (orig_dict) -> từ điển đông lạnh. dict (Frozen_dict) -> dict thông thường.

Cập nhật ngày 21 tháng 1 năm 2015: Đoạn mã gốc tôi đã đăng vào năm 2014 đã sử dụng vòng lặp for để tìm khóa khớp. Điều đó cực kỳ chậm. Bây giờ tôi đã kết hợp một triển khai tận dụng các tính năng băm của froundredet. Các cặp khóa-giá trị được lưu trữ trong các thùng chứa đặc biệt trong đó __hash____eq__ hàm chức năng chỉ dựa trên khóa. Mã này cũng đã được thử nghiệm chính thức theo đơn vị, không giống như những gì tôi đã đăng ở đây vào tháng 8 năm 2014.

Giấy phép kiểu MIT.

if 3 / 2 == 1:
    version = 2
elif 3 / 2 == 1.5:
    version = 3

def col(i):
    ''' For binding named attributes to spots inside subclasses of tuple.'''
    g = tuple.__getitem__
    @property
    def _col(self):
        return g(self,i)
    return _col

class Item(tuple):
    ''' Designed for storing key-value pairs inside
        a FrozenDict, which itself is a subclass of frozenset.
        The __hash__ is overloaded to return the hash of only the key.
        __eq__ is overloaded so that normally it only checks whether the Item's
        key is equal to the other object, HOWEVER, if the other object itself
        is an instance of Item, it checks BOTH the key and value for equality.

        WARNING: Do not use this class for any purpose other than to contain
        key value pairs inside FrozenDict!!!!

        The __eq__ operator is overloaded in such a way that it violates a
        fundamental property of mathematics. That property, which says that
        a == b and b == c implies a == c, does not hold for this object.
        Here's a demonstration:
            [in]  >>> x = Item(('a',4))
            [in]  >>> y = Item(('a',5))
            [in]  >>> hash('a')
            [out] >>> 194817700
            [in]  >>> hash(x)
            [out] >>> 194817700
            [in]  >>> hash(y)
            [out] >>> 194817700
            [in]  >>> 'a' == x
            [out] >>> True
            [in]  >>> 'a' == y
            [out] >>> True
            [in]  >>> x == y
            [out] >>> False
    '''

    __slots__ = ()
    key, value = col(0), col(1)
    def __hash__(self):
        return hash(self.key)
    def __eq__(self, other):
        if isinstance(other, Item):
            return tuple.__eq__(self, other)
        return self.key == other
    def __ne__(self, other):
        return not self.__eq__(other)
    def __str__(self):
        return '%r: %r' % self
    def __repr__(self):
        return 'Item((%r, %r))' % self

class FrozenDict(frozenset):
    ''' Behaves in most ways like a regular dictionary, except that it's immutable.
        It differs from other implementations because it doesn't subclass "dict".
        Instead it subclasses "frozenset" which guarantees immutability.
        FrozenDict instances are created with the same arguments used to initialize
        regular dictionaries, and has all the same methods.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> f['x']
            [out] >>> 3
            [in]  >>> f['a'] = 0
            [out] >>> TypeError: 'FrozenDict' object does not support item assignment

        FrozenDict can accept un-hashable values, but FrozenDict is only hashable if its values are hashable.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> hash(f)
            [out] >>> 646626455
            [in]  >>> g = FrozenDict(x=3,y=4,z=[])
            [in]  >>> hash(g)
            [out] >>> TypeError: unhashable type: 'list'

        FrozenDict interacts with dictionary objects as though it were a dict itself.
            [in]  >>> original = dict(x=3,y=4,z=5)
            [in]  >>> frozen = FrozenDict(x=3,y=4,z=5)
            [in]  >>> original == frozen
            [out] >>> True

        FrozenDict supports bi-directional conversions with regular dictionaries.
            [in]  >>> original = {'x': 3, 'y': 4, 'z': 5}
            [in]  >>> FrozenDict(original)
            [out] >>> FrozenDict({'x': 3, 'y': 4, 'z': 5})
            [in]  >>> dict(FrozenDict(original))
            [out] >>> {'x': 3, 'y': 4, 'z': 5}   '''

    __slots__ = ()
    def __new__(cls, orig={}, **kw):
        if kw:
            d = dict(orig, **kw)
            items = map(Item, d.items())
        else:
            try:
                items = map(Item, orig.items())
            except AttributeError:
                items = map(Item, orig)
        return frozenset.__new__(cls, items)

    def __repr__(self):
        cls = self.__class__.__name__
        items = frozenset.__iter__(self)
        _repr = ', '.join(map(str,items))
        return '%s({%s})' % (cls, _repr)

    def __getitem__(self, key):
        if key not in self:
            raise KeyError(key)
        diff = self.difference
        item = diff(diff({key}))
        key, value = set(item).pop()
        return value

    def get(self, key, default=None):
        if key not in self:
            return default
        return self[key]

    def __iter__(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def keys(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def values(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.value, items)

    def items(self):
        items = frozenset.__iter__(self)
        return map(tuple, items)

    def copy(self):
        cls = self.__class__
        items = frozenset.copy(self)
        dupl = frozenset.__new__(cls, items)
        return dupl

    @classmethod
    def fromkeys(cls, keys, value):
        d = dict.fromkeys(keys,value)
        return cls(d)

    def __hash__(self):
        kv = tuple.__hash__
        items = frozenset.__iter__(self)
        return hash(frozenset(map(kv, items)))

    def __eq__(self, other):
        if not isinstance(other, FrozenDict):
            try:
                other = FrozenDict(other)
            except Exception:
                return False
        return frozenset.__eq__(self, other)

    def __ne__(self, other):
        return not self.__eq__(other)


if version == 2:
    #Here are the Python2 modifications
    class Python2(FrozenDict):
        def __iter__(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def iterkeys(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def itervalues(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.value

        def iteritems(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield (i.key, i.value)

        def has_key(self, key):
            return key in self

        def viewkeys(self):
            return dict(self).viewkeys()

        def viewvalues(self):
            return dict(self).viewvalues()

        def viewitems(self):
            return dict(self).viewitems()

    #If this is Python2, rebuild the class
    #from scratch rather than use a subclass
    py3 = FrozenDict.__dict__
    py3 = {k: py3[k] for k in py3}
    py2 = {}
    py2.update(py3)
    dct = Python2.__dict__
    py2.update({k: dct[k] for k in dct})

    FrozenDict = type('FrozenDict', (frozenset,), py2)

1
Lưu ý rằng bạn cũng đã cấp phép cho nó theo CC BY-SA 3.0, bằng cách đăng nó ở đây. Ít nhất đó là quan điểm phổ biến . Tôi đoán cơ sở pháp lý cho việc đó là đồng ý với một số T & C khi bạn đăng ký lần đầu.
Evgeni Sergeev

1
Tôi đã phá vỡ bộ não của mình khi cố gắng nghĩ ra một cách để tìm kiếm hàm băm mà không cần lệnh. Xác định lại hàm băm của Itemhàm băm là một bản hack gọn gàng!
clacke

Thật không may, thời gian chạy diff(diff({key}))vẫn là tuyến tính theo kích thước của FrozenDict, trong khi thời gian truy cập dict thông thường là không đổi trong trường hợp trung bình.
Dennis

6

Tôi nghĩ về frozendict mỗi khi tôi viết một hàm như thế này:

def do_something(blah, optional_dict_parm=None):
    if optional_dict_parm is None:
        optional_dict_parm = {}

6
Mỗi lần tôi nhìn thấy một nhận xét như thế này, tôi chắc chắn rằng tôi đã viết sai ở đâu đó và đặt {} làm mặc định, và quay lại và xem mã được viết gần đây của tôi.
Ryan Hiebert

1
Vâng, đó là một vấn đề khó chịu mà mọi người gặp phải, sớm hay muộn.
Mark Visser

8
Công thức dễ dàng hơn:optional_dict_parm = optional_dict_parm or {}
Emmanuel

2
Trong trường hợp này, bạn có thể sử dụng làm giá trị mặc định cho đối số. types.MappingProxyType({})
GingerPlusPlus

@GingerPlusPlus bạn có thể viết nó lên như một câu trả lời không?
jonrsharpe

5

Bạn có thể sử dụng frozendicttừ utilspiegói như:

>>> from utilspie.collectionsutils import frozendict

>>> my_dict = frozendict({1: 3, 4: 5})
>>> my_dict  # object of `frozendict` type
frozendict({1: 3, 4: 5})

# Hashable
>>> {my_dict: 4}
{frozendict({1: 3, 4: 5}): 4}

# Immutable
>>> my_dict[1] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mquadri/workspace/utilspie/utilspie/collectionsutils/collections_utils.py", line 44, in __setitem__
    self.__setitem__.__name__, type(self).__name__))
AttributeError: You can not call '__setitem__()' for 'frozendict' object

Theo tài liệu :

frozendict (dict_obj) : Chấp nhận obj của loại dict và trả về một dictable và bất biến


5

Cài đặt frozendict

pip install frozendict

Sử dụng nó!

from frozendict import frozendict

def smth(param = frozendict({})):
    pass

3

Vâng, đây là câu trả lời thứ hai của tôi, nhưng nó là một cách tiếp cận hoàn toàn khác. Việc thực hiện đầu tiên là trong trăn thuần. Cái này ở Cython. Nếu bạn biết cách sử dụng và biên dịch các mô-đun Cython, thì đây cũng nhanh như một từ điển thông thường. Khoảng 0,04 đến 0,06 micro-giây để lấy một giá trị duy nhất.

Đây là tập tin "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']

Đây là tập tin "setup.py"

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

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

Nếu bạn đã cài đặt Cython, hãy lưu hai tệp ở trên vào cùng một thư mục. Di chuyển đến thư mục đó trong dòng lệnh.

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

Và bạn nên được thực hiện.


3

Nhược điểm chính của namedtuple là nó cần được chỉ định trước khi sử dụng, do đó không thuận tiện cho các trường hợp sử dụng một lần.

Tuy nhiên, có một cách giải quyết thực tế có thể được sử dụng để xử lý nhiều trường hợp như vậy. Giả sử bạn muốn có một tương đương bất biến của lệnh sau:

MY_CONSTANT = {
    'something': 123,
    'something_else': 456
}

Điều này có thể được mô phỏng như thế này:

from collections import namedtuple

MY_CONSTANT = namedtuple('MyConstant', 'something something_else')(123, 456)

Thậm chí có thể viết một chức năng phụ trợ để tự động hóa việc này:

def freeze_dict(data):
    from collections import namedtuple
    keys = sorted(data.keys())
    frozen_type = namedtuple(''.join(keys), keys)
    return frozen_type(**data)

a = {'foo':'bar', 'x':'y'}
fa = freeze_dict(data)
assert a['foo'] == fa.foo

Tất nhiên điều này chỉ hoạt động đối với các thiết bị phẳng, nhưng không quá khó để thực hiện một phiên bản đệ quy.


1
Vấn đề tương tự như với câu trả lời tuple khác: bạn phải làm getattr(fa, x)thay vì fa[x], không có keysphương pháp nào trong đầu ngón tay của bạn và tất cả các lý do khác mà một ánh xạ có thể được mong muốn.
Nhà vật lý điên

1

Phân lớp dict

tôi thấy mô hình này trong tự nhiên (github) và muốn đề cập đến nó:

class FrozenDict(dict):
    def __init__(self, *args, **kwargs):
        self._hash = None
        super(FrozenDict, self).__init__(*args, **kwargs)

    def __hash__(self):
        if self._hash is None:
            self._hash = hash(tuple(sorted(self.items())))  # iteritems() on py2
        return self._hash

    def _immutable(self, *args, **kws):
        raise TypeError('cannot change object - object is immutable')

    __setitem__ = _immutable
    __delitem__ = _immutable
    pop = _immutable
    popitem = _immutable
    clear = _immutable
    update = _immutable
    setdefault = _immutable

sử dụng ví dụ:

d1 = FrozenDict({'a': 1, 'b': 2})
d2 = FrozenDict({'a': 1, 'b': 2})
d1.keys() 
assert isinstance(d1, dict)
assert len(set([d1, d2])) == 1  # hashable

Ưu

  • hỗ trợ cho get(), keys(), items()( iteritems()trên py2) và tất cả các goodies từ dictra khỏi hộp mà không thực hiện chúng một cách rõ ràng
  • sử dụng nội bộ dictcó nghĩa là hiệu suất ( dictđược viết bằng c trong CPython)
  • thanh lịch đơn giản và không có phép màu đen
  • isinstance(my_frozen_dict, dict)trả về True - mặc dù python khuyến khích gõ vịt nhiều gói sử dụng isinstance(), nhưng điều này có thể lưu nhiều chỉnh sửa và tùy chỉnh

Nhược điểm

  • bất kỳ lớp con nào cũng có thể ghi đè lên điều này hoặc truy cập vào bên trong (bạn không thể thực sự bảo vệ 100% thứ gì đó trong python, bạn nên tin tưởng người dùng của mình và cung cấp tài liệu tốt).
  • Nếu bạn quan tâm đến tốc độ, bạn có thể muốn làm __hash__nhanh hơn một chút.

Tôi đã thực hiện một so sánh tốc độ trong một chủ đề khác và hóa ra, ghi đè __setitem__và kế thừa dictlà cực kỳ nhanh so với nhiều lựa chọn thay thế.
Torxed


0

Tôi cần truy cập các khóa cố định cho một thứ gì đó tại một thời điểm cho một thứ gì đó thuộc loại liên tục toàn cầu và tôi đã giải quyết vấn đề như thế này:

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

Sử dụng nó như

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

CẢNH BÁO: Tôi không khuyến nghị điều này cho hầu hết các trường hợp sử dụng vì nó khiến một số sự đánh đổi khá nghiêm trọng.


Sau đây sẽ có sức mạnh tương đương mà không có sự khan hiếm hiệu suất. Tuy nhiên, đây chỉ là đơn giản hóa câu trả lời được chấp nhận ... `` `class FrozenDict: def __init __ (self, data): self._data = data def __getitem __ (self, key): return self._data [key]` ` `
Yuval

@Yuval câu trả lời đó không tương đương. Đối với người mới bắt đầu, api là khác nhau vì nó cần dữ liệu để init. Điều này cũng ngụ ý rằng nó không còn có thể truy cập trên toàn cầu. Hơn nữa, nếu _data bị đột biến, giá trị trả về của bạn sẽ thay đổi. Tôi biết rằng có những sự đánh đổi đáng kể - như tôi đã nói, tôi không khuyến nghị điều này cho hầu hết các trường hợp sử dụng.
Trạng từ

-1

Trong trường hợp không có hỗ trợ ngôn ngữ bản địa, bạn có thể tự làm hoặc sử dụng giải pháp hiện có. May mắn thay, Python làm cho nó trở nên đơn giản để mở rộng ra các triển khai cơ sở của chúng.

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

Lớp Frozen_dict của bạn không thể
băm được
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.