Python tidak memiliki tipe frozendict bawaan. Ternyata ini tidak akan berguna terlalu sering (meskipun mungkin masih lebih bermanfaat daripada frozenset
biasanya).
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'
__hash__
metode Anda bisa sedikit ditingkatkan. Cukup gunakan variabel sementara saat menghitung hash, dan hanya menetapkanself._hash
setelah 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.Anehnya, meskipun kami jarang menggunakan
frozenset
python, 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:
Tampaknya masih agak timpang:
Di python3 Anda memiliki opsi ini :
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_config
pembaruan akanDEFAULTS
seperti 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.
sumber
def foo(config=MappingProxyType({'a': 1})):
? Contoh Anda masih memungkinkan modifikasi globaldefault_config
juga.config = default_config = {'a': 1}
adalah kesalahan ketik.Dengan asumsi kunci dan nilai kamus itu sendiri tidak dapat diubah (mis. String) maka:
sumber
dict(t)
Tidak ada
fronzedict
, tetapi Anda bisa menggunakanMappingProxyType
yang ditambahkan ke pustaka standar dengan Python 3.3:sumber
TypeError: can't pickle mappingproxy objects
Ini kode yang saya gunakan. Saya subclassed frozenset. Keuntungan dari ini adalah sebagai berikut.
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.
sumber
Item
menjadi hash key adalah hack yang rapi!diff(diff({key}))
masih linier dalam ukuran FrozenDict, sementara waktu akses dikt reguler konstan dalam kasus rata-rata.Saya memikirkan frozendict setiap kali saya menulis fungsi seperti ini:
sumber
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Anda dapat menggunakan
frozendict
dariutilspie
paket sebagai:Sesuai dokumen :
sumber
Instal frozendict
Gunakan!
sumber
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"
Ini file "setup.py"
Jika Anda menginstal Cython, simpan dua file di atas ke dalam direktori yang sama. Pindah ke direktori itu di baris perintah.
Dan Anda harus selesai.
sumber
Kerugian utama
namedtuple
adalah 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:
Ini dapat ditiru seperti ini:
Bahkan mungkin untuk menulis fungsi bantu untuk mengotomatisasi ini:
Tentu saja ini hanya berfungsi untuk dicts datar, tetapi seharusnya tidak terlalu sulit untuk menerapkan versi rekursif.
sumber
getattr(fa, x)
alih - alihfa[x]
, tidak adakeys
metode di ujung jari Anda, dan semua alasan lain pemetaan dapat diinginkan.Subklasifikasi
dict
saya melihat pola ini di alam bebas (github) dan ingin menyebutkannya:
contoh penggunaan:
Pro
get()
,keys()
,items()
(iteritems()
pada py2) dan semua barang daridict
luar kotak tanpa secara eksplisit menerapkannyadict
yang berarti kinerja (dict
ditulis dalam c di CPython)isinstance(my_frozen_dict, dict)
mengembalikan True - meskipun python mendorong penggunaan mengetik banyak paket bebekisinstance()
, ini dapat menghemat banyak penyesuaian dan penyesuaianCons
__hash__
sedikit lebih cepat.sumber
__setitem__
dan mewarisidict
sangat cepat dibandingkan dengan banyak alternatif.Opsi lain adalah
MultiDictProxy
kelas darimultidict
paket.sumber
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:
Gunakan seperti
PERINGATAN: Saya tidak merekomendasikan ini untuk sebagian besar kasus penggunaan karena membuat beberapa pengorbanan yang cukup parah.
sumber
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.
sumber