Apa yang akan "beku dict" menjadi?

158
  • Satu set beku adalah frozenset.
  • Daftar yang dibekukan bisa berupa tupel.
  • Apa yang akan menjadi beku dict? Dict yang tidak berubah dan hashable.

Saya kira itu bisa menjadi sesuatu seperti collections.namedtuple, tapi itu lebih seperti dict kunci beku (dict setengah beku). Bukan?

Sebuah "frozendict" harus kamus beku, harus memiliki keys, values, get, dll, dan dukungan in, fordll

pembaruan:
* ini dia: https://www.python.org/dev/peps/pep-0603

dugres
sumber

Jawaban:

120

Python tidak memiliki tipe frozendict bawaan. Ternyata ini tidak akan berguna terlalu sering (meskipun mungkin masih lebih bermanfaat daripada frozensetbiasanya).

Alasan paling umum untuk menginginkan jenis seperti itu adalah ketika fungsi memoizing meminta fungsi dengan argumen yang tidak diketahui. Solusi paling umum untuk menyimpan hashable equivalent dari dict (di mana nilainya hashable) adalah sesuatu seperti tuple(sorted(kwargs.iteritems())).

Ini tergantung pada penyortiran yang tidak sedikit gila. Python tidak bisa menjanjikan penyortiran secara positif akan menghasilkan sesuatu yang masuk akal di sini. (Tapi itu tidak bisa menjanjikan banyak hal lain, jadi jangan terlalu banyak berkeringat.)


Anda bisa dengan mudah membuat semacam pembungkus yang berfungsi seperti dict. Mungkin terlihat seperti

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

Ini seharusnya bekerja dengan baik:

>>> 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'
Mike Graham
sumber
7
Saya tidak tahu tingkat keamanan benang apa yang orang khawatirkan dengan hal semacam ini, tetapi dalam hal ini __hash__metode Anda bisa sedikit ditingkatkan. Cukup gunakan variabel sementara saat menghitung hash, dan hanya menetapkan self._hashsetelah Anda memiliki nilai akhir. Dengan cara itu utas lain mendapatkan hash sementara yang pertama menghitung hanya akan melakukan perhitungan redundan, daripada mendapatkan nilai yang salah.
Jeff DQ
22
@ Jeff Sebagai aturan, semua kode di mana-mana tidak aman untuk thread, dan Anda harus membungkusnya dengan beberapa struktur sinkronisasi agar dapat menggunakan kode itu dengan aman. Juga, gagasan khusus Anda tentang keselamatan ulir bergantung pada atomicity penetapan atribut objek, yang jauh dari terjamin.
Devin Jeanpierre
9
@Anentropic, Itu tidak benar sama sekali.
Mike Graham
17
Diperingatkan: "FrozenDict" ini belum tentu beku. Tidak ada yang menghentikan Anda dari menempatkan daftar yang bisa berubah sebagai nilai, dalam hal ini hashing akan menimbulkan kesalahan. Tidak ada yang salah dengan itu, tetapi pengguna harus sadar. Hal lain: Algoritma hashing ini dipilih dengan buruk, sangat rentan terhadap benturan hash. Misalnya {'a': 'b'} hash sama dengan {'b': 'a'} dan {'a': 1, 'b': 2} hash sama dengan {'a': 2, ' b ': 1}. Pilihan yang lebih baik adalah self._hash ^ = hash ((kunci, nilai))
Steve Byrnes
6
Jika Anda menambahkan entri yang bisa berubah di objek yang tidak dapat diubah, dua perilaku yang mungkin adalah melempar kesalahan saat membuat objek, atau melempar kesalahan pada hashing objek. Tuples melakukan yang terakhir, frozenset melakukan yang pertama. Saya pasti berpikir Anda membuat keputusan yang baik untuk mengambil pendekatan yang terakhir, semua hal dipertimbangkan. Namun demikian, saya berpikir bahwa orang mungkin melihat bahwa FrozenDict dan frozenset memiliki nama yang sama, dan melompat ke kesimpulan bahwa mereka harus berperilaku sama. Jadi saya pikir perlu memperingatkan orang tentang perbedaan ini. :-)
Steve Byrnes
63

Anehnya, meskipun kami jarang menggunakan frozensetpython, masih belum ada pemetaan beku. Idenya ditolak di PEP 416 - Tambahkan tipe builtin frozendict . Idenya dapat ditinjau kembali dengan Python 3.9, lihat PEP 603 - Menambahkan tipe beku peta ke koleksi .

Jadi solusi python 2 untuk ini:

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

Tampaknya masih agak timpang:

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

Di python3 Anda memiliki opsi ini :

from types import MappingProxyType

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

def foo(config=DEFAULTS):
    ...

Sekarang konfigurasi default dapat diperbarui secara dinamis, tetapi tetap tidak berubah di tempat yang Anda inginkan tidak berubah dengan memberikan proxy.

Jadi perubahan dalam default_configpembaruan akan DEFAULTSseperti yang diharapkan, tetapi Anda tidak bisa menulis ke objek proxy pemetaan itu sendiri.

Memang ini tidak sama dengan "dikte yang tidak dapat diubah dan hash" - tetapi ini adalah pengganti yang layak karena jenis kasus penggunaan yang sama dengan yang kita inginkan sebagai frozendict.

wim
sumber
2
Apakah ada alasan khusus untuk menyimpan proxy dalam variabel modul? Kenapa tidak adil def foo(config=MappingProxyType({'a': 1})):? Contoh Anda masih memungkinkan modifikasi global default_configjuga.
jpmc26
Juga, saya menduga penugasan ganda di config = default_config = {'a': 1}adalah kesalahan ketik.
jpmc26
21

Dengan asumsi kunci dan nilai kamus itu sendiri tidak dapat diubah (mis. String) maka:

>>> 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
msw
sumber
Ini adalah representasi yang baik, kanonik, dan tidak berubah dari suatu dikt (pembatasan perilaku perbandingan gila mengacaukan semacam itu).
Mike Graham
6
@devin: setuju secara penuh, tetapi saya akan membiarkan pos saya berdiri sebagai contoh bahwa seringkali ada cara yang lebih baik.
msw
14
Bahkan lebih baik jika meletakkannya di frozenset, yang tidak memerlukan kunci atau nilai untuk memiliki urutan yang konsisten.
penanggung jawab
7
Hanya satu masalah dengan ini: Anda tidak lagi memiliki pemetaan. Itu akan menjadi inti dari memiliki dict beku di tempat pertama.
Fisikawan Gila
2
Metode ini sangat bagus ketika kembali ke dikt. cukupdict(t)
codythecoder
12

Tidak ada fronzedict, tetapi Anda bisa menggunakan MappingProxyTypeyang ditambahkan ke pustaka standar dengan 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})
Julio Marins
sumber
dengan peringatan:TypeError: can't pickle mappingproxy objects
Radu
Saya suka ide ini. Saya akan mencobanya.
Doug
10

Ini kode yang saya gunakan. Saya subclassed frozenset. Keuntungan dari ini adalah sebagai berikut.

  1. Ini adalah objek yang benar-benar abadi. Tidak mengandalkan perilaku baik pengguna dan pengembang di masa depan.
  2. Sangat mudah untuk mengonversi bolak-balik antara kamus biasa dan kamus beku. FrozenDict (orig_dict) -> kamus beku. dict (frozen_dict) -> dict biasa.

Pembaruan 21 Januari 2015: Sepotong kode asli yang saya posting pada tahun 2014 menggunakan for-loop untuk menemukan kunci yang cocok. Itu sangat lambat. Sekarang saya telah mengumpulkan implementasi yang mengambil keuntungan dari fitur hashing frozenset. Pasangan nilai kunci disimpan dalam wadah khusus tempat __hash__dan __eq__fungsinya hanya didasarkan pada kunci. Kode ini juga telah diuji unit secara formal, tidak seperti yang saya posting di sini pada Agustus 2014.

Lisensi bergaya 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)
Steve Zelaznik
sumber
1
Perhatikan bahwa Anda juga telah melisensikannya di bawah CC BY-SA 3.0, dengan mempostingnya di sini. Setidaknya itulah pandangan yang lazim . Saya kira dasar hukum untuk itu menyetujui beberapa T&C ketika Anda pertama kali mendaftar.
Evgeni Sergeev
1
Aku mematahkan otakku mencoba memikirkan cara untuk mencari kunci hash tanpa diktat. Mendefinisikan ulang hash Itemmenjadi hash key adalah hack yang rapi!
clacke
Sayangnya, waktu menjalankan diff(diff({key}))masih linier dalam ukuran FrozenDict, sementara waktu akses dikt reguler konstan dalam kasus rata-rata.
Dennis
6

Saya memikirkan frozendict setiap kali saya menulis fungsi seperti ini:

def do_something(blah, optional_dict_parm=None):
    if optional_dict_parm is None:
        optional_dict_parm = {}
Mark Visser
sumber
6
Setiap kali saya melihat komentar seperti ini, saya yakin saya mengacaukan suatu tempat dan meletakkan {} sebagai default, dan kembali dan melihat kode saya yang baru ditulis.
Ryan Hiebert
1
Ya, itu adalah gotcha jahat yang ditemui semua orang, cepat atau lambat.
Mark Visser
8
Formulasi yang lebih mudah:optional_dict_parm = optional_dict_parm or {}
Emmanuel
2
Dalam hal ini Anda dapat menggunakan sebagai nilai default untuk argumen. types.MappingProxyType({})
GingerPlusPlus
@GingerPlusPlus dapatkah Anda menuliskannya sebagai jawaban?
jonrsharpe
5

Anda dapat menggunakan frozendictdari utilspiepaket sebagai:

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

Sesuai dokumen :

frozendict (dict_obj) : Menerima obj dari tipe dict dan mengembalikan dict yang hashable dan immutable

Moinuddin Quadri
sumber
5

Instal frozendict

pip install frozendict

Gunakan!

from frozendict import frozendict

def smth(param = frozendict({})):
    pass
Andrey Korchak
sumber
3

Ya, ini jawaban kedua saya, tetapi ini pendekatan yang sangat berbeda. Implementasi pertama adalah python murni. Yang ini ada di Cython. Jika Anda tahu cara menggunakan dan mengkompilasi modul Cython, ini sama cepatnya dengan kamus biasa. Secara kasar .04 hingga .06 mikro-detik untuk mengambil nilai tunggal.

Ini adalah file "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']

Ini file "setup.py"

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

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

Jika Anda menginstal Cython, simpan dua file di atas ke dalam direktori yang sama. Pindah ke direktori itu di baris perintah.

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

Dan Anda harus selesai.

Steve Zelaznik
sumber
3

Kerugian utama namedtupleadalah bahwa itu perlu ditentukan sebelum digunakan, jadi itu kurang nyaman untuk kasus sekali pakai.

Namun, ada solusi praktis yang dapat digunakan untuk menangani banyak kasus seperti itu. Katakanlah Anda ingin memiliki padanan abadi dari dikt berikut:

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

Ini dapat ditiru seperti ini:

from collections import namedtuple

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

Bahkan mungkin untuk menulis fungsi bantu untuk mengotomatisasi ini:

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

Tentu saja ini hanya berfungsi untuk dicts datar, tetapi seharusnya tidak terlalu sulit untuk menerapkan versi rekursif.

Berislav Lopac
sumber
1
Masalah yang sama dengan jawaban tuple lainnya: Anda harus melakukan getattr(fa, x)alih - alih fa[x], tidak ada keysmetode di ujung jari Anda, dan semua alasan lain pemetaan dapat diinginkan.
Fisikawan Gila
1

Subklasifikasi dict

saya melihat pola ini di alam bebas (github) dan ingin menyebutkannya:

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

contoh penggunaan:

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

Pro

  • dukungan untuk get(), keys(), items()( iteritems()pada py2) dan semua barang dari dictluar kotak tanpa secara eksplisit menerapkannya
  • menggunakan internal dictyang berarti kinerja ( dictditulis dalam c di CPython)
  • elegan sederhana dan tanpa sihir hitam
  • isinstance(my_frozen_dict, dict)mengembalikan True - meskipun python mendorong penggunaan mengetik banyak paket bebekisinstance() , ini dapat menghemat banyak penyesuaian dan penyesuaian

Cons

  • subkelas apa pun dapat mengesampingkan ini atau mengaksesnya secara internal (Anda benar-benar tidak dapat melindungi sesuatu dengan python, Anda harus mempercayai pengguna Anda dan memberikan dokumentasi yang baik).
  • jika Anda peduli kecepatan, Anda mungkin ingin membuatnya __hash__sedikit lebih cepat.
ShmulikA
sumber
Saya melakukan perbandingan kecepatan di utas lain dan ternyata, mengesampingkan __setitem__dan mewarisi dictsangat cepat dibandingkan dengan banyak alternatif.
Torxed
0

Opsi lain adalah MultiDictProxykelas dari multidictpaket.

Berislav Lopac
sumber
1
Sayangnya, ini bukan hashable.
rominf
0

Saya perlu mengakses kunci tetap untuk sesuatu pada satu titik untuk sesuatu yang merupakan jenis hal yang konstan secara global dan saya memutuskan sesuatu seperti ini:

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

Gunakan seperti

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

PERINGATAN: Saya tidak merekomendasikan ini untuk sebagian besar kasus penggunaan karena membuat beberapa pengorbanan yang cukup parah.

Adverbia
sumber
Yang berikut ini akan memiliki kekuatan yang sama tanpa skarifikasi kinerja. Namun, ini hanyalah penyederhanaan dari jawaban yang diterima ... `` `class FrozenDict: def __init __ (self, data): self._data = data def __getitem __ (self, key): return self._data [key]` ` `
Yuval
@Yuval jawaban itu tidak setara. Sebagai permulaan api berbeda karena membutuhkan data untuk init. Ini juga menyiratkan bahwa itu tidak lagi dapat diakses secara global. Lebih jauh, jika _data dimutasi, nilai pengembalian Anda berubah. Saya menyadari bahwa ada pengorbanan yang signifikan - seperti yang saya katakan, saya tidak merekomendasikan ini untuk kebanyakan kasus penggunaan.
Adverbly
-1

Dengan tidak adanya dukungan bahasa asli, Anda dapat melakukannya sendiri atau menggunakan solusi yang ada. Untungnya Python membuatnya sangat mudah untuk memperluas implementasi basis mereka.

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
efreezy
sumber
Kelas frozen_dict Anda tidak hashable
miracle173