Hapus elemen dari kamus

1396

Apakah ada cara untuk menghapus item dari kamus dengan Python?

Selain itu, bagaimana saya bisa menghapus item dari kamus untuk mengembalikan salinan (yaitu, tidak memodifikasi yang asli)?

richzilla
sumber
13
Mengapa Anda memerlukan fungsi yang mengembalikan kamus, padahal Anda bisa langsung memodifikasi kamus?
amillerrhodes
5
The kamus popmetode perubahan kamus di tempat . Oleh karena itu mengubah referensi ke kamus yang diteruskan dari pemanggil ke "fungsi pembantu". Jadi "fungsi pembantu" tidak perlu mengembalikan apa pun, karena referensi asli ke kamus di pemanggil akan sudah diubah. Jangan berikan pengembalian dari dict.pop()apa pun jika Anda tidak membutuhkannya. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Gunakan deepcopy(my_dict)jika dibutuhkan.
Mark Mikofski
1
Karena judul aslinya tidak setuju dengan detail dan secara khusus mengecualikan solusi yang jelas d.pop(), saya memperbaiki judul untuk mengajukan pertanyaan yang ditentukan dalam detail.
smci
1
Kita harus menambahkan peringatan yang menanyakan apakah Anda benar - benar ingin melakukan ini, seolah-olah Anda melakukannya N kali pada kamus dengan elemen E Anda akan bocor (/ gunakan) memori O (N * E) dengan semua salinan yang dalam. Jika Anda hanya ingin read-only (salinan dangkal), lakukan d.pop(key). Tetapi jika ada yang memodifikasi salinan dangkal, Anda memiliki masalah yang terkenal dengan alias . Ini membantu jika Anda memberi tahu kami konteks yang lebih luas. (Apakah ada hal lain yang memodifikasi nilai dict? Apakah Anda mencoba untuk secara iterate mengulangi daftar? Jika tidak, apa?)
smci
5
"Mengapa Anda memerlukan fungsi yang mengembalikan kamus, padahal Anda bisa langsung memodifikasi kamus?" Mungkin karena Anda ingin menulis fungsi murni yang tidak mengubah parameternya?
Gene Callahan

Jawaban:

1729

The delpernyataan menghilangkan unsur:

del d[key]

Namun, ini memutasi kamus yang ada sehingga konten kamus berubah untuk siapa pun yang memiliki referensi ke instance yang sama. Untuk mengembalikan kamus baru , buat salinan kamus:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

The dict()konstruktor membuat salinan dangkal . Untuk membuat salinan yang dalam, lihat copymodul .


Perhatikan bahwa membuat salinan untuk setiap dikt del/ tugas / dll. berarti Anda beralih dari waktu konstan ke waktu linier, dan juga menggunakan ruang linear. Untuk dikte kecil, ini bukan masalah. Tetapi jika Anda berencana untuk membuat banyak salinan dari dikte besar, Anda mungkin menginginkan struktur data yang berbeda, seperti HAMT (seperti yang dijelaskan dalam jawaban ini ).

Greg Hewgill
sumber
15
itulah poin bagus tentang mutabilitas kamus +1 - meskipun saya tidak dapat memikirkan saat ketika saya ingin salinan kamus, saya selalu mengandalkan salinan 'semua orang' sama. poin bagus.
tMC
30
@ tMC Jika Anda mengedit dictsaat Anda mengulanginya, itu akan memberi Anda kesalahan:RuntimeError: dictionary changed size during iteration
VertigoRay
15
Bagaimana dengan popmetode yang sebenarnya melakukan hal yang sama? Bukankah ini lebih pythonic? (menjadi metode dict, bukan kata khusus yang dipesan)?
Serge
21
Jawaban ini memiliki kelemahan, bisa jadi menyesatkan. Pembaca mungkin salah paham bahwa dikt (d) dapat memberi mereka salinan dengan 'd'. Tapi ini salinan yang tidak lengkap. Ketika hanya melakukan operasi tombol del, tidak apa-apa. Tetapi ketika Anda ingin melakukan sesuatu yang lain pada dict bersarang, memodifikasi 'r' menggunakan metode salin dapat menyebabkan perubahan ke 'd' yang asli. Untuk mendapatkan salinan asli, Anda harus terlebih dahulu 'mengimpor salinan', dan kemudian 'r = copy.deepcopy (d)'.
Zen
10
@ Zen: Cukup adil, saya telah menambahkan catatan tentang salinan dangkal vs dalam.
Greg Hewgill
264

pop mengubah kamus.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

Jika Anda ingin menyimpan yang asli, Anda bisa menyalinnya.

Kristal
sumber
29
"del" tidak apa-apa, tapi "pop" tampaknya lebih "Pythonic", menurut saya.
ivanleoncz
1
@ vanleoncz mengapa?
kevr
3
popmengembalikan nilai yang 'muncul', yang memungkinkan Anda menggunakan nilai ini untuk alasan apa pun lebih lanjut. Jika tidak lebih "Pythonic", saya akan mengatakan itu tampaknya lebih baik, pasti :). Ini bukan dict, tetapi bekerja dengan cara yang sama untuk keduanya: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
ivanleoncz
2
@ivanleoncz Ini juga lebih baik untuk satu alasan lagi, popdapat diberikan dengan nilai default yang akan dikembalikan ketika kunci hilang dari dict. Ada baiknya ketika Anda perlu menghapus beberapa tombol tetapi beberapa dari mereka mungkin hilang; delakan menyerah KeyErrordalam kasus seperti itu.
itachi
80

Saya pikir solusi Anda adalah cara terbaik untuk melakukannya. Tetapi jika Anda menginginkan solusi lain, Anda dapat membuat kamus baru dengan menggunakan kunci dari kamus lama tanpa menyertakan kunci yang Anda tentukan, seperti ini:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}
utdemir
sumber
2
Benar-benar keren. Saya suka metode cepat untuk menyaring kamus tanpa mendefinisikan fungsi baru.
Joe J
6
Bagi mereka yang tidak terbiasa dengan pemahaman, Anda juga dapat melakukan sesuatu seperti ini: {i:a[i] for i in a if i not in [0, 1, 2]}jika Anda ingin menghapus beberapa elemen.
kmatheny
9
Lebih baik {k:v for k,v in a.items() if k != 0}menurut saya.
rlbond
1
Solusi terbaik untuk menghapus item dengan kunci dan mengembalikan hasil dict baru di baris yang sama. Misalnya jika Anda perlu menggunakan dict yang sudah dibangun tanpa item tunggal sebagai **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole
Solusi terbaik di sini. Satu liner dan itu tidak mengubah kamus asli.
Andrew Winterbotham
55

The del pernyataan adalah apa yang Anda cari. Jika Anda memiliki kamus bernama foo dengan kunci yang disebut 'bar', Anda dapat menghapus 'bar' dari foo seperti ini:

del foo['bar']

Perhatikan bahwa ini secara permanen memodifikasi kamus yang sedang dioperasikan. Jika Anda ingin menyimpan kamus asli, Anda harus membuat salinan sebelumnya:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

The dictpanggilan membuat salinan dangkal. Jika Anda ingin salinan yang dalam, gunakan copy.deepcopy.

Inilah metode yang dapat Anda salin & tempel, untuk kenyamanan Anda:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
arussell84
sumber
1
@ pythonian29033, sebenarnya, tidak . Jawaban yang diterima berfungsi seperti yang diharapkan - ia mengembalikan dict tanpa satu kunci. Pendekatan dari jawaban ini bermutasi dict asli;) Ada perbedaan yang signifikan
maxkoryukov
1
@ arussell84, mengapa >>>sering digunakan dalam contoh python? Ya, python-doc mengandung banyak hal seperti itu. Tetapi kode seperti itu tidak nyaman untuk copypaste . Saya bingung ...
maxkoryukov
@maxkoryukov ya itu! tetapi fungsi itu dan jawaban ini persis sama, dengan pengecualian jawaban itu berada di dalam suatu fungsi. dan Anda tidak boleh mengkode dalam python untuk sementara waktu, >>>meniru notasi mendengarkan dari python dalam mode cli
pythonian29033
2
@ pythonian29033 tentang >>>. Ya, ini gaya REPL, tapi mari kita bicara terus terang: satu-satunya orang yang menulis sampel ini, dan 1000 orang telah membaca ini. Saya pikir, akan sangat bagus untuk menulis contoh dengan cara yang memungkinkan menyalin dan menjalankan dengan mudah. Saya tidak suka menghapus kurung sudut ini dengan tangan. Atau salin baris demi baris .. Jadi saya tidak mengerti: mengapa sudut ini masih ada))) Mungkin saya tidak tahu sesuatu?
maxkoryukov
3
Saya telah menambahkan fungsi yang dapat disalin / ditempelkan, untuk kenyamanan Anda.
arussell84
48

Ada banyak jawaban yang bagus, tetapi saya ingin menekankan satu hal.

Anda dapat menggunakan dict.pop()metode dan delpernyataan yang lebih umum untuk menghapus item dari kamus. Keduanya mengubah kamus asli, jadi Anda perlu membuat salinan (lihat detail di bawah).

Dan keduanya akan menaikkan KeyErrorjika kunci yang Anda berikan kepada mereka tidak ada dalam kamus:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

dan

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

Anda harus mengurus ini:

dengan menangkap pengecualian:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

dan

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

dengan melakukan pemeriksaan:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

dan

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

tetapi dengan pop()ada juga cara yang jauh lebih ringkas - memberikan nilai pengembalian default:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

Kecuali Anda menggunakan pop()untuk mendapatkan nilai kunci yang dihapus Anda dapat memberikan apa pun, tidak perlu None. Meskipun mungkin menggunakan deldengan inmemeriksa sedikit lebih cepat karena pop()menjadi fungsi dengan komplikasinya sendiri yang menyebabkan overhead. Biasanya tidak demikian, sehingga pop()dengan nilai default cukup baik.


Sedangkan untuk pertanyaan utama, Anda harus membuat salinan kamus Anda, untuk menyimpan kamus asli dan memiliki yang baru tanpa kunci dihapus.

Beberapa orang lain di sini menyarankan untuk membuat salinan lengkap (dalam) copy.deepcopy(), yang mungkin berlebihan, salinan "normal" (dangkal), menggunakan copy.copy()atau dict.copy(), mungkin cukup. Kamus menyimpan referensi ke objek sebagai nilai untuk kunci. Jadi ketika Anda menghapus kunci dari kamus referensi ini dihapus, bukan objek yang direferensikan. Objek itu sendiri dapat dihapus nanti secara otomatis oleh pengumpul sampah, jika tidak ada referensi lain untuk itu di memori. Membuat salinan yang dalam membutuhkan lebih banyak perhitungan dibandingkan dengan salinan yang dangkal, sehingga mengurangi kinerja kode dengan membuat salinan, membuang-buang memori dan memberikan lebih banyak pekerjaan ke GC, kadang-kadang salinan yang dangkal sudah cukup.

Namun, jika Anda memiliki objek yang bisa berubah-ubah sebagai nilai kamus dan berencana untuk mengubahnya nanti di kamus yang dikembalikan tanpa kunci, Anda harus membuat salinan yang dalam.

Dengan salinan dangkal:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

Dengan salinan dalam:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}
Nikita
sumber
21

... bagaimana saya bisa menghapus item dari kamus untuk mengembalikan salinan (yaitu, tidak memodifikasi yang asli)?

A dictadalah struktur data yang salah untuk digunakan untuk ini.

Tentu, menyalin dict dan muncul dari copy berfungsi, dan begitu pula membangun dict baru dengan pemahaman, tetapi semua penyalinan itu membutuhkan waktu — Anda telah mengganti operasi waktu-konstan dengan operasi waktu-linear. Dan semua salinan itu hidup sekaligus mengambil ruang — ruang linear per salinan.

Struktur data lainnya, seperti upaya hash dipetakan , dirancang untuk kasus penggunaan jenis ini: menambah atau menghapus elemen akan mengembalikan salinan dalam waktu logaritmik, berbagi sebagian besar penyimpanannya dengan yang asli . 1

Tentu ada beberapa kelemahannya. Performanya lebih logaritmik daripada konstan (walaupun dengan basis besar, biasanya 32-128). Dan, sementara Anda dapat membuat API non-mutasi identik dengan dict, API "bermutasi" jelas berbeda. Dan, yang paling penting, tidak ada baterai HAMT yang disertakan dengan Python. 2

The pyrsistentperpustakaan adalah implementasi yang cukup padat berbasis HAMT dict-pengganti (dan berbagai jenis lainnya) untuk Python. Ia bahkan memiliki API evolver yang bagus untuk memindahkan kode mutasi yang ada ke kode persisten semulus mungkin. Tetapi jika Anda ingin secara eksplisit tentang mengembalikan salinan daripada bermutasi, Anda cukup menggunakannya seperti ini:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

Itulah d3 = d1.remove('a')tepatnya pertanyaan yang diajukan.

Jika Anda memiliki struktur data yang dapat diubah-ubah seperti dictdan listdisematkan di dalamnya pmap, Anda masih akan memiliki masalah alias — Anda hanya dapat memperbaikinya dengan terus berubah sepanjang jalan, menyematkan pmaps dan pvectors.


1. HAMT juga menjadi populer dalam bahasa seperti Scala, Clojure, Haskell karena mereka bermain sangat baik dengan pemrograman bebas kunci dan memori transaksional perangkat lunak, tetapi tak satu pun dari keduanya sangat relevan dengan Python.

2. Bahkan, ada adalah sebuah HAMT di stdlib, yang digunakan dalam pelaksanaan contextvars. PEP yang ditarik sebelumnya menjelaskan alasannya. Tapi ini adalah detail implementasi tersembunyi dari perpustakaan, bukan jenis koleksi publik.

abarnert
sumber
19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Hasil: d = {1: 2, '2': 3}

satelit
sumber
14

Cukup panggil del d ['key'].

Namun, dalam produksi, selalu merupakan praktik yang baik untuk memeriksa apakah 'kunci' ada di d.

if 'key' in d:
    del d['key']
Khanh Hua
sumber
7
Hmm, tidak, dalam produksi lebih baik mengikuti ideologi EAFP . Hapus saja kunci dalam try-exceptblokir. Setidaknya, ini akan menjadi operasi atom;)
maxkoryukov
1
Dan jika Anda ingin ringkas - gunakan d.pop('key', None), itu adalah oneliner. Tetapi pertanyaan sebenarnya adalah tentang mendapatkan kamus tanpa satu kunci, dan bukan tentang memodifikasi dict. Jadi pemahaman - adalah pilihan yang baik di sini;)
maxkoryukov
7

Tidak, tidak ada jalan lain selain itu

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

Namun, sering membuat salinan dari hanya kamus yang sedikit diubah mungkin bukan ide yang baik karena akan menghasilkan permintaan memori yang relatif besar. Biasanya lebih baik untuk log kamus lama (jika perlu) dan kemudian memodifikasinya.

phihag
sumber
7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
Daino3
sumber
5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

ini tidak melakukan penanganan kesalahan, ia menganggap kuncinya ada dalam dikt, Anda mungkin ingin memeriksa itu terlebih dahulu dan raisejika tidak

tMC
sumber
10
Bagaimana metode Anda berbeda dari sekadar del test_dict[key]?
Fisikawan Gila
5

Berikut pendekatan desain tingkat atas:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

Saya meneruskan kamus dan kunci yang saya inginkan ke dalam fungsi saya, memvalidasi apakah itu kamus dan jika kuncinya baik-baik saja, dan jika keduanya ada, menghapus nilai dari kamus dan mencetak sisa-overs.

Keluaran: {'B': 55, 'A': 34}

Semoga itu bisa membantu!

atlas
sumber
3

Cuplikan kode di bawah ini akan membantu Anda dengan pasti, saya telah menambahkan komentar di setiap baris yang akan membantu Anda dalam memahami kode.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

atau Anda juga dapat menggunakan dict.pop ()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

atau pendekatan yang lebih baik

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}
Mayur Agarwal
sumber
2

Berikut variasi lain menggunakan pemahaman daftar:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

Pendekatan ini didasarkan pada jawaban dari pos ini: Cara yang efisien untuk menghapus kunci dengan string kosong dari dict

BigBlueHat
sumber
1
Jika Anda akan menjawab pertanyaan lama yang sudah memiliki jawaban yang sederhana, sesuai, diterima, setidaknya pastikan jawaban Anda benar. Ini tidak melakukan apa yang diminta OP.
user2357112 mendukung Monica
Saya biasanya tidak memeriksa tanggal pada pertanyaan yang saya pikir bisa menambahkan info berharga kepada mereka. Selain itu, per salah satu komentar pada pertanyaan yang saya tautkan ke: "Biasanya ini adalah apa yang diinginkan seseorang dan mungkin apa yang dibutuhkan OP, tetapi bukan itu yang diminta OP untuk" stackoverflow.com/questions/12118695/… I tahu itu bukan jawaban langsung untuk pertanyaan itu; bukan ekspansi ke opsi.
BigBlueHat
3
Jawaban ini, meskipun tidak lengkap, memungkinkan kami mengetahui bahwa kami dapat menghapus item dengan kondisi jika terlalu. hanya mengubah if vuntuk if k is not 'a'menjawab op. Tapi saya tidak berpikir itu cara yang efisien, ini menghilangkan elemen dalam O (n) daripada O (log n) seperti yang dilakukan pop atau del.
holgac
0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

Kode berikut akan membuat salinan dict speciesdan menghapus item yang tidak adatrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Sama saja
sumber