Usuń element ze słownika

1392

Czy istnieje sposób na usunięcie elementu ze słownika w Pythonie?

Dodatkowo, jak mogę usunąć element ze słownika, aby zwrócić kopię (tj. Nie modyfikując oryginału)?

Richzilla
źródło
13
Dlaczego potrzebujesz funkcji, która zwraca słownik, skoro możesz bezpośrednio zmodyfikować słownik?
amillerrhodes
5
Słowniku popmetoda zmienia słownika w miejscu . Dlatego zmienia odniesienie do słownika, który został przekazany przez program wywołujący do „funkcji pomocnika”. Zatem „funkcja pomocnika” nie musi niczego zwracać, ponieważ oryginalne odwołanie do słownika w wywołującym zostanie już zmienione. Nie przypisuj zwrotu z dict.pop()niczego, jeśli go nie potrzebujesz. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Użyj w deepcopy(my_dict)razie potrzeby.
Mark Mikofski
1
Ponieważ oryginalny tytuł nie zgadzał się ze szczegółami i wyraźnie wykluczył oczywiste rozwiązanie d.pop(), poprawiłem tytuł, aby zadać pytanie określone w szczegółach.
smci
1
Powinniśmy dodać zastrzeżenie z pytaniem, czy naprawdę chcesz to zrobić, tak jakbyś robił to N razy w słowniku z elementami E, wyciekasz (/ używasz) pamięci O (N * E) wszystkimi głębokimi kopiami. Jeśli chcesz tylko do odczytu (płytka kopia), zrób to d.pop(key). Ale jeśli cokolwiek zmodyfikuje płytką kopię, masz dobrze znany problem z aliasingiem . Pomaga, jeśli powiesz nam szerszy kontekst. (Czy coś jeszcze zmienia wartości dykta? Czy próbujesz destrukcyjnie iterować listę? Jeśli nie, co?)
smci
5
„Dlaczego potrzebujesz funkcji, która zwraca słownik, skoro możesz po prostu bezpośrednio zmodyfikować słownik?” Być może dlatego, że chcesz pisać czyste funkcje, które nie modyfikują ich parametrów?
Gene Callahan

Odpowiedzi:

1725

delOświadczenie usuwa element:

del d[key]

Powoduje to jednak mutację istniejącego słownika, więc zawartość słownika zmienia się dla każdego, kto ma odwołanie do tej samej instancji. Aby zwrócić nowy słownik, wykonaj jego kopię:

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

dict()Konstruktor robi płytkie kopię . Aby wykonać głęboką kopię, zobacz copymoduł .


Pamiętaj, że wykonanie kopii dla każdego nagrania del/ zadania / etc. oznacza, że ​​przechodzisz od czasu stałego do czasu liniowego, a także używasz przestrzeni liniowej. W przypadku małych nagrań nie stanowi to problemu. Ale jeśli planujesz wykonać wiele kopii dużych nagrań, prawdopodobnie potrzebujesz innej struktury danych, na przykład HAMT (jak opisano w tej odpowiedzi ).

Greg Hewgill
źródło
15
to świetna uwaga na temat zmienności słowników +1 - chociaż nie mogę myśleć o czasach, kiedy chciałem kopii słownika, zawsze polegałem na tym, że kopia „wszystkich” jest taka sama. świetny punkt.
tMC
30
@tMC Jeśli edytujesz dictpętlę, pojawi się błąd:RuntimeError: dictionary changed size during iteration
VertigoRay
15
Co z popmetodą, która w rzeczywistości robi to samo? Czy to nie jest bardziej pytoniczne? (jest to metoda dykta, a nie specjalne słowo zastrzeżone)?
Serge
21
Ta odpowiedź ma słabość, może być myląca. Czytelnicy mogą nie rozumieć, że dict (d) może dać im kopię z „d”. Ale to niekompletna kopia. Jeśli wykonujesz tylko operacje usuwania kluczy, to jest OK. Ale jeśli chcesz zrobić coś innego dla zagnieżdżonego słownika, modyfikacja „r” przy użyciu tej metody kopiowania może spowodować zmianę na oryginalne „d”. Aby uzyskać autentyczną kopię, potrzebujesz najpierw „importuj kopię”, a następnie „r = copy.deepcopy (d)”.
Zen
10
@Zen: W porządku, dodałem notatkę o płytkiej kontra głębokiej kopii.
Greg Hewgill
264

pop mutuje słownik.

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

Jeśli chcesz zachować oryginał, możesz go po prostu skopiować.

Kryształ
źródło
29
„del” jest w porządku, ale moim zdaniem „pop” wydaje się bardziej „Pythonic”.
ivanleoncz
1
@ivanleoncz dlaczego?
kevr
3
popzwraca wartość „pękniętą”, co pozwala na użycie tej wartości z dowolnego innego powodu. Jeśli nie jest to bardziej „Pythonic”, powiedziałbym, że wydaje się to lepsze na pewno :). To nie jest dyktando, ale działa tak samo dla obu: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
ivanleoncz
2
@ivanleoncz Jest to również lepsze z jeszcze jednego powodu, popmożna podać domyślną wartość, która zostanie zwrócona, gdy brakuje klucza w nagraniu. Dobrze jest, gdy trzeba usunąć kilka kluczy, ale niektórych z nich może brakować; delrzuciłby KeyErrorw takim przypadku.
itachi
80

Myślę, że twoje rozwiązanie jest najlepszym sposobem na to. Ale jeśli chcesz innego rozwiązania, możesz utworzyć nowy słownik przy użyciu kluczy ze starego słownika bez dołączania określonego klucza, na przykład:

>>> 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
źródło
2
Bardzo fajny. Podoba mi się szybka metoda filtrowania słownika bez definiowania nowej funkcji.
Joe J
6
Dla tych, którzy nie znają rozumienia, możesz również zrobić coś takiego: {i:a[i] for i in a if i not in [0, 1, 2]}jeśli chcesz usunąć kilka elementów.
kmatheny
9
{k:v for k,v in a.items() if k != 0}Myślę, że lepiej by było .
rlbond
1
Najlepsze rozwiązanie do usuwania elementu kluczem i zwracania wyniku nowego dykta w tej samej linii. Na przykład, jeśli chcesz użyć już skonstruowanego nagrania bez pojedynczego elementu **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole,
Najlepsze rozwiązanie tutaj. Jeden linijka i nie mutuje oryginalnego słownika.
Andrew Winterbotham,
55

Instrukcja del jest tym, czego szukasz. Jeśli masz słownik o nazwie foo z kluczem o nazwie „bar”, możesz usunąć „bar” z foo w następujący sposób:

del foo['bar']

Zauważ, że to trwale modyfikuje obsługiwany słownik. Jeśli chcesz zachować oryginalny słownik, musisz wcześniej utworzyć kopię:

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

dictRozmowa sprawia płytką kopię. Jeśli chcesz głębokiej kopii, użyj copy.deepcopy.

Oto metoda, którą możesz skopiować i wkleić dla Twojej wygody:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
arussell84
źródło
1
@ pythonian29033, właściwie nie . Przyjęta odpowiedź działa zgodnie z oczekiwaniami - zwraca dyktando bez jednego klucza. Podejście z tej odpowiedzi mutuje oryginalny dykt;) Istnieje znacząca różnica
maxkoryukov
1
@ arussell84, dlaczego >>>często jest używany w przykładach python? Tak, python-doc zawiera wiele takich rzeczy. Ale taki kod nie jest wygodny w przypadku kopiowania . Jestem zdezorientowany ...
maxkoryukov
@maxkoryukov tak to robi! ale ta funkcja i ta odpowiedź są dokładnie takie same, z wyjątkiem tego, że odpowiedź znajduje się w funkcji. i przez pewien czas nie >>>
musiałeś pisać
2
@ pythonian29033 about >>>. Tak, jest w stylu REPL, ale porozmawiajmy szczerze: jedyny człowiek napisał tę próbkę, a 1000 ją przeczytało. Myślę, że dobrze byłoby pisać przykłady w sposób umożliwiający łatwe kopiowanie i uruchamianie. Nie lubię ręcznie usuwać kątowników. Lub kopiuj wiersz po wierszu .. Więc nie rozumiem: dlaczego te kąty wciąż tam są))) Być może coś nie wiem?
maxkoryukov
3
Dla Twojej wygody dodałem funkcję, którą można skopiować / wkleić.
arussell84
48

Jest wiele fajnych odpowiedzi, ale chcę podkreślić jedną rzecz.

Możesz użyć zarówno dict.pop()metody, jak i bardziej ogólnej delinstrukcji, aby usunąć elementy ze słownika. Obaj mutują oryginalny słownik, więc musisz zrobić kopię (zobacz szczegóły poniżej).

I oba podniosą, KeyErrorjeśli klucz, który im podajesz, nie jest obecny w słowniku:

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

i

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

Musisz się tym zająć:

przechwytując wyjątek:

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)

i

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)

poprzez sprawdzenie:

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

i

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

ale pop()jest też o wiele bardziej zwięzły sposób - podaj domyślną wartość zwracaną:

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

O ile nie użyjesz go pop()do odzyskania wartości klucza, możesz podać wszystko, co nie jest konieczne None. Chociaż może się zdarzyć, że używanie delz inkontrolą jest nieco szybsze, ponieważ pop()jest funkcją z własnymi komplikacjami powodującymi narzut. Zwykle tak nie jest, więc pop()przy domyślnej wartości jest wystarczająco dobra.


Jeśli chodzi o główne pytanie, będziesz musiał zrobić kopię swojego słownika, aby zapisać oryginalny słownik i mieć nowy bez wyjmowania klucza.

Niektórzy inni ludzie sugerują tutaj wykonanie pełnej (głębokiej) kopii copy.deepcopy(), która może być przesadą, „normalną” (płytką) kopią, używając copy.copy()lub dict.copy(), może wystarczyć. Słownik przechowuje odwołanie do obiektu jako wartość klucza. Kiedy więc usuniesz klucz ze słownika, odwołanie to zostanie usunięte, a nie odwołany do obiektu. Sam obiekt może zostać później automatycznie usunięty przez moduł wyrzucający elementy bezużyteczne, jeśli nie ma w nim innych odniesień. Wykonywanie głębokiej kopii wymaga więcej obliczeń w porównaniu z płytką kopią, więc zmniejsza wydajność kodu, tworząc kopię, marnując pamięć i zapewniając więcej pracy GC, czasami płytka kopia wystarcza.

Jeśli jednak masz zmienne obiekty jako wartości słownikowe i planujesz zmodyfikować je później w zwróconym słowniku bez klucza, musisz wykonać głęboką kopię.

Z płytką kopią:

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}

Z głęboką kopią:

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
źródło
20

… Jak mogę usunąć element ze słownika, aby zwrócić kopię (tj. Nie modyfikując oryginału)?

A dictjest złą strukturą danych do użycia w tym celu.

Jasne, kopiowanie nagrania i wyskakiwanie z kopii działa, podobnie jak budowanie nowego nagrania ze zrozumieniem, ale całe to kopiowanie wymaga czasu - zastąpiłeś operację w czasie stałym operacją w czasie liniowym. Wszystkie żywe kopie jednocześnie zajmują miejsce - przestrzeń liniowa na kopię.

Inne struktury danych, takie jak próby mapowania tablicy skrótów , są zaprojektowane do tego rodzaju przypadków użycia: dodanie lub usunięcie elementu zwraca kopię w czasie logarytmicznym, dzieląc większość miejsca z oryginałem . 1

Oczywiście są pewne wady. Wydajność jest raczej logarytmiczna niż stała (chociaż z dużą bazą, zwykle 32-128). I chociaż można uczynić niezmutujący interfejs API identycznym z dict, „mutujący” interfejs API jest oczywiście inny. A przede wszystkim w Pythonie nie ma akumulatorów HAMT. 2)

pyrsistentBiblioteka jest całkiem solidne wdrożenie HAMT opartych DICT-zamienniki (i różnych innych typów) dla Pythona. Ma nawet niezły interfejs API do ewolucji do przenoszenia istniejącego kodu mutującego na trwały kod tak płynnie, jak to możliwe. Ale jeśli chcesz wyraźnie mówić o zwrocie kopii, a nie o mutacji, użyj jej w następujący sposób:

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

Właśnie d3 = d1.remove('a')o to pyta pytanie.

Jeśli masz zmienne struktury danych, takie jak dicti listosadzone w nich pmap, nadal będziesz mieć problemy z aliasingiem - możesz to naprawić, przechodząc niezmiennie do końca, osadzając pmaps i pvectors.


1. HAMT stały się również popularne w językach takich jak Scala, Clojure, Haskell, ponieważ grają bardzo ładnie dzięki programowaniu bez blokady i pamięci transakcyjnej oprogramowania, ale żaden z nich nie jest bardzo istotny w Pythonie.

2. W rzeczywistości jest stdlib HAMT, używany do implementacji contextvars. Wcześniej wycofany PEP wyjaśnia, dlaczego. Jest to jednak ukryty szczegół implementacji biblioteki, a nie typ kolekcji publicznej.

abarnert
źródło
19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

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

satelity
źródło
14

Wystarczy wywołać del d [„klucz”].

Jednak w produkcji zawsze dobrą praktyką jest sprawdzanie, czy „d” istnieje w d.

if 'key' in d:
    del d['key']
Khanh Hua
źródło
7
Hmm, nie, w produkcji lepiej jest podążać za ideologią EAFP . Po prostu wyjmij klucz w try-exceptbloku. Przynajmniej będzie to operacja atomowa;)
maxkoryukov
1
A jeśli chcesz być zwięzły - użyj d.pop('key', None), to oneliner. Ale rzeczywiste pytanie dotyczyło uzyskania słownika bez jednego klucza, a nie modyfikacji słownika. Więc zrozumienie - to dobry wybór tutaj;)
maxkoryukov
7

Nie, nie ma innego wyjścia niż

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

Jednak często tworzenie kopii tylko nieznacznie zmienionych słowników prawdopodobnie nie jest dobrym pomysłem, ponieważ spowoduje stosunkowo duże zapotrzebowanie na pamięć. Zwykle lepiej jest zalogować stary słownik (jeśli to konieczne), a następnie zmodyfikować go.

phihag
źródło
7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
daino3
źródło
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}
>>>

nie powoduje to żadnej obsługi błędów, zakłada, że ​​klucz znajduje się w słowie, możesz najpierw to sprawdzić, a raisejeśli nie

tMC
źródło
10
Czym różni się Twoja metoda od zwykłej del test_dict[key]?
Mad Physicist,
5

Oto podejście do projektowania na najwyższym poziomie:

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

Przekazuję słownik i klucz, który chcę, do mojej funkcji, sprawdza, czy jest to słownik i czy klucz jest w porządku, a jeśli oba istnieją, usuwa wartość ze słownika i drukuje resztki.

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

Mam nadzieję, że to pomaga!

atlas
źródło
3

Poniższy fragment kodu zdecydowanie ci pomoże, dodałem komentarze w każdym wierszu, które pomogą ci w zrozumieniu kodu.

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

lub możesz także użyć dict.pop ()

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

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

lub lepsze jest podejście

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
źródło
2

Oto kolejna odmiana wykorzystująca rozumienie listy:

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

Podejście to opiera się na odpowiedzi z tego postu: Skuteczny sposób usuwania kluczy z pustych ciągów ze słownika

BigBlueHat
źródło
1
Jeśli zamierzasz odpowiedzieć na wieloletnie pytanie, które ma już prostą, odpowiednią, zaakceptowaną odpowiedź, przynajmniej upewnij się, że twoja odpowiedź jest prawidłowa. To nie robi tego, o co poprosił PO.
user2357112 obsługuje Monikę
Zasadniczo nie sprawdzam dat w pytaniach, które moim zdaniem mogłyby zawierać cenne informacje. Dodatkowo, na jeden z komentarzy do pytania, które zamieściłem: „Zazwyczaj jest to dokładnie to, czego ktoś chce i prawdopodobnie tego potrzebuje OP, ale nie jest to, o co OP poprosił” stackoverflow.com/questions/12118695/... I wiedział, że nie jest to bezpośrednia odpowiedź na pytanie; raczej rozszerzenie opcji.
BigBlueHat
3
Ta odpowiedź, choć niepełna, pozwala nam dowiedzieć się, że możemy usuwać przedmioty, jeśli warunki też są spełnione. tylko zmieniam się, if vby if k is not 'a'odpowiedzieć na op. Ale nie sądzę, że jest to skuteczny sposób, ponieważ usuwa element w O (n) zamiast O (log n), tak jak pop lub 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)}}

Poniższy kod utworzy kopię nagrania speciesi usunie elementy, których nie matrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Sameeresque
źródło