Sözlükten bir öğeyi silme


1394

Python'daki bir sözlükten bir öğe silmenin bir yolu var mı?

Ayrıca, bir kopyasını döndürmek için bir öğeyi sözlükten nasıl silebilirim (orijinali değiştirmemek için)?


13
Sözlüğü doğrudan değiştirebildiğinizde neden sözlük döndüren bir işleve ihtiyacınız var?
amillerrhodes

5
Sözlük popyöntem sözlüğünü değiştirir yerinde . Bu nedenle, arayandan "yardımcı işlev" e aktarılan sözlüğe başvuruyu değiştirir . Bu yüzden "yardımcı fonksiyon" un hiçbir şey döndürmesi gerekmez, çünkü arayandaki sözlüğe orijinal referans zaten değiştirilmiş olacaktır. dict.pop()İhtiyacınız yoksa hiçbir şeye dönüşü atamayın . EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Gerekirse kullanın deepcopy(my_dict).
Mark Mikofski

1
Orijinal başlık ayrıntılarla aynı fikirde olmadığından ve bariz çözümü özellikle hariç tuttuğundan d.pop(), ayrıntılarda belirtilen soruyu sormak için başlığı düzelttim.
smci

1
Bunu gerçekten yapmak isteyip istemediğinizi soran bir uyarı eklemeliyiz , sanki E öğeleri içeren bir sözlükte N kez yaparsanız, tüm derin kopyalarla O (N * E) belleği sızdırırsınız (/ kullanırsınız). Yalnızca salt okunur (sığ bir kopya) istiyorsanız, bunu yapın d.pop(key). Ancak herhangi bir şey sığ kopyayı değiştirirse, takma adda iyi bilinen bir sorununuz vardır . Bize daha geniş bağlamı anlatırsanız yardımcı olur. (Karar değerlerini değiştiren başka bir şey var mı? Bir liste üzerinde yıkıcı bir şekilde yinelemeye mi çalışıyorsunuz? Değilse, ne?)
SMCI

5
"Neden sözlüğü doğrudan değiştirebildiğinizde neden sözlük döndüren bir işleve ihtiyacınız var?" Belki saf fonksiyonları yazmak istiyorum çünkü yok parametrelerini değiştirmek?
Gene Callahan

Yanıtlar:


1728

delİfadesi bir öğeyi kaldırır:

del d[key]

Ancak bu, varolan sözlüğü değiştirir, böylece sözlüğün içeriği aynı örneğe başvurusu olan herkes için değişir. Yeni bir sözlük döndürmek için sözlüğün bir kopyasını oluşturun:

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

dict()Yapıcı bir hale getirir sığ kopya . Derin bir kopya oluşturmak için copymodüle bakın .


Her karar del/ ödev / vb. İçin bir kopya oluşturmayı unutmayın . sabit zamandan doğrusal zamana gideceğiniz ve aynı zamanda doğrusal boşluk kullandığınız anlamına gelir. Küçük zorluklar için bu bir sorun değildir. Ancak, büyük diksellerin çok sayıda kopyasını yapmayı planlıyorsanız, muhtemelen HAMT gibi farklı bir veri yapısı istersiniz ( bu cevapta açıklandığı gibi ).


15
sözlüklerin +1 değişebilirliği hakkında büyük bir nokta - ben sözlük kopyalarını istedim bir zaman düşünemiyorum olsa da, ben her zaman 'herkesin' kopya aynı olması güveniyor. harika bir nokta.
tMC

30
@tMC Eğer üzerinden dolaşırken düzenlerseniz, dictsize bir hata verir:RuntimeError: dictionary changed size during iteration
VertigoRay

15
popAslında aynı olan yöntem ne olacak ? Daha pitonik değil mi? (dikte yöntemi olmak, özel ayrılmış kelime değil)?
Serge

21
Bu cevabın bir zayıflığı var, yanıltıcı olabilir. Okuyucular, (d) 'nin' d 'ile bir kopyasını verebileceğini yanlış anlayabilirler. Ama eksik bir kopya. Sadece del tuşları işlemleri yaparken, sorun değil. Ancak iç içe bir dikte başka bir şey yapmak istediğinizde, bu kopyalama yöntemini kullanarak 'r' yi değiştirmek orijinal 'd' de değişikliğe neden olabilir. Özgün bir kopya almak için önce 'içe aktarılan kopyaya', ardından 'r = copy.deepcopy (d)' ye ihtiyacınız vardır.
Zen

10
@Zen: Yeterince adil, sığ ve derin kopya hakkında bir not ekledim.
Greg Hewgill

264

pop sözlüğü değiştirir.

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

Orijinali saklamak istiyorsanız, kopyalayabilirsiniz.


29
"del" tamam, ama "pop" Bence daha "Pythonic" görünüyor.
ivanleoncz

1
@ivanleoncz neden?
kevr

3
pop"patlatılmış" değeri döndürür; bu, başka bir nedenden dolayı bu değeri kullanmanıza olanak tanır. Daha fazla "Pythonic" değilse, daha iyi görünüyor eminim :). Bir diksiyon
ivanleoncz

2
@ivanleoncz Ayrıca bir nedenden dolayı daha iyidir, bir anahtarda dikte popeksik olduğunda döndürülecek varsayılan bir değer sağlanabilir. Birkaç anahtarı kaldırmanız gerektiğinde iyi olur, ancak bazıları eksik olabilir; böyle bir durumda delatacaktı KeyError.
itachi

80

Çözümünüzün bunu yapmanın en iyi yolu olduğunu düşünüyorum. Ancak başka bir çözüm istiyorsanız, belirttiğiniz anahtarı dahil etmeden eski sözlükteki anahtarları kullanarak yeni bir sözlük oluşturabilirsiniz:

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

2
Gerçekten havalı. Yeni bir işlev tanımlamadan sözlüğü filtrelemek için hızlı yöntemi seviyorum.
Joe J

6
Anlayışlara aşina olmayanlar için, böyle bir şey de yapabilirsiniz: {i:a[i] for i in a if i not in [0, 1, 2]}birkaç öğeyi kaldırmak istiyorsanız.
kmatheny

9
{k:v for k,v in a.items() if k != 0}Bence daha iyi olur .
rlbond

1
Bir öğeyi anahtarla kaldırmak ve yeni diktenin sonucunu aynı satırda döndürmek için en iyi çözüm. Örneğin, önceden oluşturulmuş bir dikdörtgeni tek bir öğe olmadan kullanmanız gerekiyorsa **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole

Burada en iyi çözüm. Bir astar ve orijinal sözlüğü değiştirmez.
Andrew Winterbotham

55

Del deyimi aradığınız budur. 'Bar' adında bir anahtarla foo adında bir sözlüğünüz varsa, 'bar' ı foo'dan şu şekilde silebilirsiniz:

del foo['bar']

Bunun, üzerinde çalışmakta olan sözlüğü kalıcı olarak değiştirdiğini unutmayın. Orijinal sözlüğü korumak istiyorsanız, önceden bir kopya oluşturmanız gerekir:

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

dictÇağrı basit bir kopyasını yapar. Derin bir kopya istiyorsanız kullanın copy.deepcopy.

Size kolaylık sağlamak için kopyalayıp yapıştırabileceğiniz bir yöntem:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy

1
@ pythonian29033, aslında hayır . Kabul edilen cevap beklendiği gibi çalışır - tek bir anahtar olmadan dikteyi döndürür. Bu cevabın yaklaşımı orijinal
dikteyi değiştirir

1
@ arussell84, neden >>>python örneklerinde sıklıkla kullanılır? Evet, python-doc pek çok şey içeriyor. Ancak bu kod kopya macunu için uygun değildir . Kafam karıştı ...
maxkoryukov

@maxkoryukov evet! ancak bu fonksiyon ve bu cevap, aynı cevabın bir fonksiyonun içinde olması dışında, tamamen aynıdır. ve bir süredir >>>
python'da kodlamamalısınız

2
@ pythonian29033 hakkında >>>. Evet, bu REPL tarzı, ama açık bir şekilde konuşalım: bu örneği yazan tek kişi ve 1000 kişi bunu okudu. Bence kolay kopyalama ve çalıştırma imkanı sağlayacak örnekler yazmak harika olurdu. Bu köşeli parantezleri elle çıkarmak istemiyorum. Veya satır satır kopyalayın .. Yani anlamıyorum: neden bu açılar hala orada))) olabilir bir şey bilmiyorum?
maxkoryukov

3
Size kolaylık sağlamak için kopyalanabilen / yapıştırılabilen bir işlev ekledim.
arussell84

48

Çok güzel cevaplar var, ama bir şeyi vurgulamak istiyorum.

Sözlükteki öğeleri kaldırmak için hem dict.pop()yöntemi hem de daha genel bir delifadeyi kullanabilirsiniz. Her ikisi de orijinal sözlüğü değiştirir, bu nedenle bir kopya oluşturmanız gerekir (aşağıdaki ayrıntılara bakın).

Ve her ikisi de, onlara KeyErrorsağladığınız anahtar sözlükte yoksa:

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

ve

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

Bununla ilgilenmelisin:

istisnayı yakalayarak:

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)

ve

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)

bir çek yaparak:

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

ve

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

ancak pop()çok daha kısa bir yol da var - varsayılan dönüş değerini belirtin:

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

pop()Kaldırılmakta olan bir anahtarın değerini almak için kullanmadığınız sürece , gerekli olmayan herhangi bir şey sağlayabilirsiniz None. O kullanarak bu olabilir rağmen delile inkontrol olduğunu biraz daha hızlı nedeniyle pop()kendi komplikasyonlar havai neden olan bir fonksiyon olmak. Genellikle durum böyle değildir, bu nedenle pop()varsayılan değer yeterli.


Ana soruya gelince, orijinal sözlüğü kaydetmek ve anahtar kaldırılmadan yeni bir tane edinmek için sözlüğünüzün bir kopyasını oluşturmanız gerekir.

Buradaki diğer bazı kişiler copy.deepcopy(), bir overkill, "normal" (sığ) bir kopyayla, tam veya (derin) bir kopya almayı önerir copy.copy()veya veya kullanarak dict.copy()yeterli olabilir. Sözlük, nesneye yapılan başvuruyu bir anahtarın değeri olarak tutar. Dolayısıyla, bir sözlükten bir anahtarı kaldırdığınızda, referans alınan nesne değil, bu referans kaldırılır. Bellekte başka bir referans yoksa, nesnenin kendisi daha sonra çöp toplayıcı tarafından otomatik olarak kaldırılabilir. Derin bir kopya yapmak sığ kopyaya göre daha fazla hesaplama gerektirir, bu nedenle kopya yaparak, belleği boşa harcayarak ve GC'ye daha fazla çalışma sağlayarak kod performansını düşürür, bazen sığ kopya yeterlidir.

Ancak, sözlük değerleri olarak değiştirilebilir nesneleriniz varsa ve bunları daha sonra döndürülen sözlükte anahtar olmadan değiştirmeyi planlıyorsanız, derin bir kopya oluşturmanız gerekir.

Sığ kopya ile:

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}

Derin kopya ile:

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}

20

… Bir kopyayı döndürmek için bir öğeyi sözlükten nasıl silebilirim (orijinali değiştirmemek için)?

A dict, bunun için kullanılacak yanlış veri yapısıdır.

Elbette, dikteyi kopyalamak ve kopyadan patlamak işe yarıyor ve aynı zamanda bir anlayışla yeni bir diksiyon inşa etmek de işe yarıyor, ancak tüm bu kopyalama zaman alıyor - sabit zamanlı bir operasyonu doğrusal zamanlı bir operasyonla değiştirdiniz. Ve tüm bu kopyalar aynı anda yer kaplar - kopya başına doğrusal alan.

Karma dizi eşlenmiş denemeleri gibi diğer veri yapıları tam olarak bu tür bir kullanım durumu için tasarlanmıştır: bir öğe eklemek veya kaldırmak, kopyasının çoğunu orijinaliyle paylaşarak logaritmik bir zamanda bir kopya döndürür . 1

Tabii ki bazı dezavantajları var. Performans sabit yerine logaritmiktir (büyük bir tabana sahip olmasına rağmen, genellikle 32-128). Ayrıca, mutasyona uğramayan API'yi aynı hale getirmenize rağmen dict, "mutasyona uğrayan" API açıkça farklıdır. Ve en önemlisi, Python ile birlikte gelen HAMT pilleri yok. 2

pyrsistentKütüphane Python için hamt tabanlı dict değiştirmeler (ve diğer çeşitli türleri) arasında oldukça sağlam bir uygulamasıdır. Mevcut mutasyon kodunu kalıcı koda olabildiğince sorunsuz bir şekilde taşımak için şık bir evrim API'sı bile var . Ancak, mutasyon yapmak yerine kopyaları iade etme konusunda açık olmak istiyorsanız, sadece şu şekilde kullanın:

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

Bu d3 = d1.remove('a')soru soran tam olarak ne olduğunu.

Eğer benzeri dictve listiçine gömülü veri yapılarınız pmapvarsa, yine de diğer adlandırma sorunlarınız olacaktır - bunu yalnızca tamamen değiştirilip pmaps ve pvectors'yi gömerek düzeltebilirsiniz .


1. HAMT'ler, Scala, Clojure, Haskell gibi dillerde de popüler hale geldi çünkü kilitsiz programlama ve yazılım işlem belleği ile çok iyi oynuyorlar, ancak bunların hiçbiri Python'da çok alakalı değil.

2. Aslında, orada olduğu uygulanmasında kullanılan stdlib bir hamt, contextvars. Daha önce çekilen PEP bunun nedenini açıklar. Ancak bu, genel bir koleksiyon türü değil, kütüphanenin gizli bir uygulama detayıdır.


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

Sonuç: d = {1: 2, '2': 3}


14

Sadece del d ['key'] öğesini arayın.

Ancak, üretimde, d'de 'anahtar' olup olmadığını kontrol etmek her zaman iyi bir uygulamadır.

if 'key' in d:
    del d['key']

7
Hmm, hayır, üretimde EAFP ideolojisini takip etmek daha iyidir . Sadece try-exceptbloktaki anahtarı çıkarın . En azından, bu bir atomik işlem olacak;)
maxkoryukov

1
Ve özlü olmak istiyorsanız - kullanın d.pop('key', None), oneliner. Ancak asıl soru, sözlüğü tek bir anahtar olmadan almak ve dikteyi değiştirmekle ilgili değildi. Yani anlayışlar - burada iyi bir seçimdir;)
maxkoryukov

7

Hayır, başka yolu yok

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

Bununla birlikte, genellikle sadece biraz değiştirilmiş sözlüklerin kopyalarını oluşturmak muhtemelen iyi bir fikir değildir, çünkü nispeten büyük bellek talepleriyle sonuçlanacaktır. Eski sözlüğü (gerekirse) günlüğe kaydetmek ve sonra değiştirmek genellikle daha iyidir.


7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)

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

bu herhangi bir hata işleme yapmaz, anahtarın dikte olduğunu varsayar, önce bunu kontrol etmek isteyebilirsiniz ve raiseeğer değilse


10
Metodunuz nasıl farklıdır del test_dict[key]?
Mad Physicist

5

İşte üst düzey tasarım yaklaşımı:

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

Sözlüğü ve istediğim anahtarı işlevime geçiriyorum, sözlük olup olmadığını ve anahtarın iyi olup olmadığını doğrular ve her ikisi de varsa, sözlükten değeri kaldırır ve soldaki çıktıları yazdırır.

Çıktı: {'B': 55, 'A': 34}

Umarım yardımcı olur!


3

Aşağıda kod snippet'i kesinlikle size yardımcı olacaktır, her satıra kodu anlamanıza yardımcı olacak yorumlar ekledim.

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

veya dict.pop () yöntemini de kullanabilirsiniz

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

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

ya da daha iyi yaklaşım

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}

2

Liste kavrayışı kullanan başka bir varyasyon:

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

Yaklaşım bu yazının cevabına dayanıyor: Bir boştan dize olan anahtarları kaldırmanın etkili bir yolu


1
Zaten basit, uygun, kabul edilmiş bir cevabı olan bir yaşındaki soruyu cevaplayacaksanız, en azından cevabınızın doğru olduğundan emin olun. Bu OP'nin istediği şeyi yapmaz.
user2357112 Monica

Genelde onlara değerli bilgiler eklenebileceğini düşündüğüm soruların tarihlerini kontrol etmiyorum. Ayrıca, bağlandığım soruya yapılan yorumlardan biri başına: "Genellikle bu tam olarak birisinin istediği ve muhtemelen OP'nin ihtiyacı olan şeydir, ancak OP'nin istediği şey değildir" stackoverflow.com/questions/12118695/… I sorunun doğrudan bir cevap olmadığını biliyordu; daha çok seçeneklere genişleme.
BigBlueHat

3
Bu cevap, tam olmamakla birlikte, eğer koşullarla da öğeleri kaldırabileceğimizi öğrenmemizi sağlar. Sadece değişen if viçin if k is not 'a'cevaplar op. Ama bunun etkili bir yol olduğunu düşünmüyorum, bu öğeyi pop (del) gibi O (log n) yerine O (n) 'de kaldırır.
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)}}

Aşağıdaki kod, diktenin bir kopyasını oluşturur speciesve içinde olmayan öğeleri silertrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.