การลบหลายคีย์ออกจากพจนานุกรมอย่างปลอดภัย


128

ฉันรู้ว่าจะลบรายการ 'คีย์' ออกจากพจนานุกรมของฉันdอย่างปลอดภัยคุณทำ:

if d.has_key('key'):
    del d['key']

อย่างไรก็ตามฉันต้องการลบหลายรายการออกจากพจนานุกรมอย่างปลอดภัย ฉันกำลังคิดที่จะกำหนดรายการในทูเพิลเพราะฉันจะต้องทำมากกว่าหนึ่งครั้ง

entitiesToREmove = ('a', 'b', 'c')
for x in entitiesToRemove:
    if d.has_key(x):
        del d[x]

อย่างไรก็ตามฉันสงสัยว่ามีวิธีที่ชาญฉลาดกว่านี้หรือไม่?


3
เวลาในการดึงข้อมูลจากพจนานุกรมเกือบ O (1) เนื่องจากมีการแฮช ฉันไม่คิดว่าคุณจะทำได้ดีกว่านี้มาก
ncmathsadist

1
คำตอบของ @mattbornski ดูเหมือนจะเป็นที่ยอมรับมากกว่าและรวบรัดด้วย
Ioannis Filippidis

2
StackOverflow พูดแล้ว: key in dเป็น Pythonic มากกว่าd.has_key(key) stackoverflow.com/questions/1323410/has-key-or-in
Michael Scheper

for x in set(d) & entities_to_remove: del d[x]หากคุณสามารถสำรองบิตของหน่วยความจำที่คุณสามารถทำได้ สิ่งนี้จะมีประสิทธิภาพมากขึ้นหากentities_to_removeมีขนาด "ใหญ่" เท่านั้น
DylanYoung

คำตอบ:


56

ทำไมไม่เป็นเช่นนี้:

entries = ('a', 'b', 'c')
the_dict = {'b': 'foo'}

def entries_to_remove(entries, the_dict):
    for key in entries:
        if key in the_dict:
            del the_dict[key]

Mattbornski มีเวอร์ชันที่กะทัดรัดกว่าโดยใช้dict.pop ()


14
เพิ่มสิ่งนี้สำหรับผู้ที่มาจากเครื่องมือค้นหา หากทราบคีย์ (เมื่อความปลอดภัยไม่ใช่ปัญหา) สามารถลบคีย์ได้หลายรายการในบรรทัดเดียวเช่นนี้del dict['key1'], dict['key2'], dict['key3']
Tirtha R

ขึ้นอยู่กับจำนวนคีย์ที่คุณกำลังลบอาจมีประสิทธิภาพมากกว่าในการใช้for key in set(the_dict) & entries:และข้ามการkey in dictทดสอบ
DylanYoung

236
d = {'some':'data'}
entriesToRemove = ('any', 'iterable')
for k in entriesToRemove:
    d.pop(k, None)

38
นี้. นี่คือทางเลือกที่ชาญฉลาดของ Pythonista dict.pop()ไม่จำเป็นต้องมีการทดสอบการมีอยู่ของคีย์ ยอดเยี่ยม
Cecil Curry

4
สำหรับสิ่งที่คุ้มค่าฉันคิดว่า.pop()ไม่ดีและไม่น่ากลัวและต้องการคำตอบที่ยอมรับมากกว่าคำตอบนี้
Arne

5
ผู้คนจำนวนมากดูเหมือนจะไม่สนใจสิ่งนี้ :) ฉันไม่สนใจบรรทัดพิเศษสำหรับการตรวจสอบการมีอยู่เป็นการส่วนตัวและอ่านได้ง่ายกว่ามากเว้นแต่คุณจะรู้จักป๊อป () อยู่แล้ว ในทางกลับกันหากคุณพยายามทำสิ่งนี้ด้วยความเข้าใจหรืออินไลน์แลมบ์ดาเคล็ดลับนี้อาจช่วยได้มาก ฉันยังบอกด้วยว่าในความคิดของฉันเป็นเรื่องสำคัญที่จะต้องพบปะผู้คนในทุกที่ ฉันไม่แน่ใจว่า "ไม่ดีและไม่น่ากลัว" จะช่วยให้ผู้ที่กำลังอ่านคำตอบเหล่านี้ได้รับคำแนะนำที่ใช้ได้จริงที่พวกเขากำลังมองหา
mattbornski

5
มีเหตุผลที่ดีเป็นพิเศษในการใช้สิ่งนี้ ในขณะที่การเพิ่มบรรทัดพิเศษอาจช่วยเพิ่ม "ความสามารถในการอ่าน" หรือ "ความชัดเจน" แต่ก็เพิ่มการค้นหาเพิ่มเติมในพจนานุกรมด้วย setdefaultวิธีการนี้เป็นเทียบเท่าการกำจัดของการทำ หากนำไปใช้อย่างถูกต้อง (และฉันแน่ใจว่าเป็นเช่นนั้น) มันจะทำการค้นหาเพียงครั้งเดียวในแผนที่แฮชที่เป็นdictแทนที่จะเป็นสองรายการ
Mad Physicist

2
โดยส่วนตัวแล้วฉันจะกังวลกับความถูกต้องและการบำรุงรักษาเป็นอันดับแรกและความเร็วก็ต่อเมื่อได้รับการพิสูจน์แล้วว่าเร็วไม่เพียงพอ ความแตกต่างของความเร็วระหว่างการดำเนินการเหล่านี้จะไม่สำคัญเมื่อซูมออกไปที่ระดับแอปพลิเคชัน อาจเป็นกรณีที่เร็วกว่า แต่ฉันคาดหวังว่าในการใช้งานจริงคุณจะไม่สังเกตหรือไม่สนใจและหากคุณสังเกตเห็นและใส่ใจคุณจะได้รับการบริการที่ดีกว่าในการเขียนสิ่งที่มีประสิทธิภาพมากกว่า Python
mattbornski

90

การใช้ความเข้าใจ Dict

final_dict = {key: t[key] for key in t if key not in [key1, key2]}

โดยที่key1และkey2จะถูกลบออก

ในตัวอย่างด้านล่างคีย์ "b" และ "c" จะถูกลบออกและเก็บไว้ในรายการคีย์

>>> a
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> keys = ["b", "c"]
>>> print {key: a[key] for key in a if key not in keys}
{'a': 1, 'd': 4}
>>> 

4
พจนานุกรมใหม่? รายการเข้าใจ? คุณควรปรับคำตอบให้กับคนที่ถามคำถาม;)
กลาสโลส

6
โซลูชันนี้ได้รับผลกระทบอย่างมากเมื่อตัวแปรที่ถืออยู่มีการใช้งานเพิ่มเติมในโปรแกรม กล่าวอีกนัยหนึ่งคำสั่งที่คีย์ถูกลบออกไปนั้นมีประสิทธิภาพมากกว่าคำสั่งที่สร้างขึ้นใหม่พร้อมกับรายการที่เก็บไว้
Apalala

14
เพื่อประโยชน์ในการอ่านฉันขอแนะนำ {k: v for k, v in t.items () ถ้า k ไม่อยู่ใน [key1, key2]}
Frederic Bazin

8
นอกจากนี้ยังมีปัญหาด้านประสิทธิภาพเมื่อรายการคีย์ใหญ่เกินไปเนื่องจากการค้นหาเกิดO(n)ขึ้น การดำเนินการทั้งหมดเป็นO(mn)ที่mคือจำนวนของคีย์ใน Dict และnจำนวนของปุ่มในรายการ ฉันขอแนะนำให้ใช้ชุด{key1, key2}แทนถ้าเป็นไปได้
ldavid

4
ถึง Apalala: คุณช่วยให้ฉันเข้าใจได้ไหมว่าทำไมถึงมีผลงานยอดฮิต
Sean

21

โซลูชันกำลังใช้mapและfilterฟังก์ชัน

หลาม 2

d={"a":1,"b":2,"c":3}
l=("a","b","d")
map(d.__delitem__, filter(d.__contains__,l))
print(d)

หลาม 3

d={"a":1,"b":2,"c":3}
l=("a","b","d")
list(map(d.__delitem__, filter(d.__contains__,l)))
print(d)

คุณได้รับ:

{'c': 3}

สิ่งนี้ใช้ไม่ได้กับฉันกับ python 3.4:>>> d={"a":1,"b":2,"c":3} >>> l=("a","b","d") >>> map(d.__delitem__, filter(d.__contains__,l)) <map object at 0x10579b9e8> >>> print(d) {'a': 1, 'b': 2, 'c': 3}
Risadinha

@Risadinha list(map(d.__delitem__,filter(d.__contains__,l))).... ในฟังก์ชัน python 3.4 map return a iterator
Jose Ricardo Bustos M.

4
หรือdeque(map(...), maxlen=0)เพื่อหลีกเลี่ยงการสร้างรายการไม่มีค่า นำเข้าครั้งแรกด้วยfrom collections import deque
Jason

19

หากคุณต้องการดึงค่าสำหรับคีย์ที่คุณกำลังลบออกนี่เป็นวิธีที่ดีทีเดียวที่จะทำ:

valuesRemoved = [d.pop(k, None) for k in entitiesToRemove]

แน่นอนว่าคุณยังคงทำสิ่งนี้ได้เพียงเพื่อลบคีย์ออกdแต่คุณจะสร้างรายการค่าด้วยความเข้าใจรายการโดยไม่จำเป็น นอกจากนี้ยังไม่มีความชัดเจนเล็กน้อยที่จะใช้ความเข้าใจในรายการเพียงเพื่อผลข้างเคียงของฟังก์ชัน


3
หรือถ้าคุณต้องการเก็บรายการที่ถูกลบไว้เป็นพจนานุกรม: valuesRemoved = dict((k, d.pop(k, None)) for k in entitiesToRemove)และอื่น ๆ
kindall

คุณสามารถละทิ้งการกำหนดให้กับตัวแปรได้ ด้วยวิธีนี้หรือแบบนั้นมันเป็นวิธีการแก้ปัญหาที่สั้นและไพโธนิกที่สุดและควรทำเครื่องหมายเป็นคำตอบหลัก IMHO
Gerhard Hagerer

12

พบวิธีแก้ปัญหาด้วยpopและmap

d = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'b', 'c']
list(map(d.pop, keys))
print(d)

ผลลัพธ์ของสิ่งนี้:

{'d': 'valueD'}

ฉันตอบคำถามนี้ช้าไปเพียงเพราะฉันคิดว่ามันจะช่วยได้ในอนาคตหากมีใครค้นหาแบบเดียวกัน และนี่อาจช่วยได้

ปรับปรุง

โค้ดด้านบนจะทำให้เกิดข้อผิดพลาดหากไม่มีคีย์ใน dict

DICTIONARY = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'l', 'c']

def remove_keys(key):
    try:
        DICTIONARY.pop(key, None)
    except:
        pass  # or do any action

list(map(remove_key, keys))
print(DICTIONARY)

เอาท์พุท:

DICTIONARY = {'b': 'valueB', 'd': 'valueD'}

1
คำตอบนี้จะทำให้เกิดข้อยกเว้นหากไม่มีคีย์ใดkeysในd- คุณจะต้องกรองสิ่งนั้นก่อน
ingofreyer

@ingofreyer อัปเดตรหัสสำหรับการจัดการข้อยกเว้น ขอขอบคุณที่ค้นหาปัญหานี้ ฉันคิดว่าตอนนี้คงได้ผล :)
Shubham Srivastava

ขอบคุณสิ่งนี้จะช่วยให้ทุกคนพบคำตอบนี้ :-)
ingofreyer

การสร้างรายการเป็นผลพลอยได้จากการใช้แผนที่ทำให้สิ่งนี้ค่อนข้างช้าการวนซ้ำจะดีกว่าจริงๆ
Charlie Clark

4

ฉันไม่มีปัญหากับคำตอบใด ๆ ที่มีอยู่ แต่ฉันประหลาดใจที่ไม่พบวิธีแก้ปัญหานี้:

keys_to_remove = ['a', 'b', 'c']
my_dict = {k: v for k, v in zip("a b c d e f g".split(' '), [0, 1, 2, 3, 4, 5, 6])}

for k in keys_to_remove:
    try:
        del my_dict[k]
    except KeyError:
        pass

assert my_dict == {'d': 3, 'e': 4, 'f': 5, 'g': 6}

หมายเหตุ: ผมเจอคำถามนี้มาจากที่นี่ และคำตอบของฉันเกี่ยวข้องกับคำตอบนี้


3

ทำไมจะไม่ล่ะ:

entriestoremove = (2,5,1)
for e in entriestoremove:
    if d.has_key(e):
        del d[e]

ฉันไม่รู้ว่าคุณหมายถึงอะไร "วิธีที่ชาญฉลาดกว่า" แน่นอนว่ามีวิธีอื่น ๆ อาจจะมีความเข้าใจในพจนานุกรม:

entriestoremove = (2,5,1)
newdict = {x for x in d if x not in entriestoremove}

2

แบบอินไลน์

import functools

#: not key(c) in d
d = {"a": "avalue", "b": "bvalue", "d": "dvalue"}

entitiesToREmove = ('a', 'b', 'c')

#: python2
map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove)

#: python3

list(map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove))

print(d)
# output: {'d': 'dvalue'}

2

การทดสอบเวลาบางอย่างสำหรับ cpython 3 แสดงให้เห็นว่าการวนซ้ำแบบง่ายเป็นวิธีที่เร็วที่สุดและอ่านได้ค่อนข้างดี การเพิ่มฟังก์ชั่นไม่ทำให้เกิดค่าใช้จ่ายมากเกินไป:

ผลลัพธ์ตามเวลา (การทำซ้ำ 10k):

  • all(x.pop(v) for v in r) # 0.85
  • all(map(x.pop, r)) # 0.60
  • list(map(x.pop, r)) # 0.70
  • all(map(x.__delitem__, r)) # 0.44
  • del_all(x, r) # 0.40
  • <inline for loop>(x, r) # 0.35
def del_all(mapping, to_remove):
      """Remove list of elements from mapping."""
      for key in to_remove:
          del mapping[key]

สำหรับการทำซ้ำเล็กน้อยการทำ 'อินไลน์' นั้นเร็วกว่าเล็กน้อยเนื่องจากค่าใช้จ่ายในการเรียกฟังก์ชัน แต่del_allปลอดภัยไม่เป็นขุยใช้ซ้ำได้และเร็วกว่าโครงสร้างการทำความเข้าใจและการทำแผนที่ของหลามทั้งหมด


0

ฉันคิดว่าการใช้คีย์สามารถถือว่าเป็นชุดเป็นวิธีที่ดีที่สุดหากคุณใช้ python 3:

def remove_keys(d, keys):
    to_remove = set(keys)
    filtered_keys = d.keys() - to_remove
    filtered_values = map(d.get, filtered_keys)
    return dict(zip(filtered_keys, filtered_values))

ตัวอย่าง:

>>> remove_keys({'k1': 1, 'k3': 3}, ['k1', 'k2'])
{'k3': 3}

0

จะเป็นการดีที่จะได้รับการสนับสนุนอย่างเต็มที่สำหรับวิธีการตั้งค่าสำหรับพจนานุกรม (และไม่ใช่เรื่องที่ไม่บริสุทธิ์ที่เราได้รับจาก Python 3.9) เพื่อให้คุณสามารถ "ลบ" ชุดคีย์ได้ อย่างไรก็ตามตราบใดที่ไม่เป็นเช่นนั้นและคุณมีพจนานุกรมขนาดใหญ่ที่มีคีย์จำนวนมากให้ลบคุณอาจต้องการทราบเกี่ยวกับประสิทธิภาพ ดังนั้นฉันได้สร้างโค้ดบางอย่างที่สร้างบางสิ่งที่ใหญ่พอสำหรับการเปรียบเทียบที่มีความหมาย: เมทริกซ์ 100,000 x 1,000 ดังนั้นรวม 10,000,00 รายการ

from itertools import product
from time import perf_counter

# make a complete worksheet 100000 * 1000
start = perf_counter()
prod = product(range(1, 100000), range(1, 1000))
cells = {(x,y):x for x,y in prod}
print(len(cells))

print(f"Create time {perf_counter()-start:.2f}s")
clock = perf_counter()
# remove everything above row 50,000

keys = product(range(50000, 100000), range(1, 100))

# for x,y in keys:
#     del cells[x, y]

for n in map(cells.pop, keys):
    pass

print(len(cells))
stop = perf_counter()
print(f"Removal time {stop-clock:.2f}s")

10 ล้านรายการขึ้นไปไม่ใช่เรื่องผิดปกติในการตั้งค่าบางอย่าง การเปรียบเทียบสองวิธีในเครื่องในพื้นที่ของฉันฉันเห็นการปรับปรุงเล็กน้อยเมื่อใช้งานmapและpopน่าจะเป็นเพราะการเรียกใช้ฟังก์ชันน้อยลง แต่ทั้งสองอย่างใช้เวลาประมาณ 2.5 วินาทีในเครื่องของฉัน แต่สิ่งนี้จะเกิดขึ้นเมื่อเทียบกับเวลาที่ต้องใช้ในการสร้างพจนานุกรมตั้งแต่แรก (55 วินาที) หรือรวมถึงการตรวจสอบภายในลูป หากเป็นไปได้ควรสร้างชุดที่เป็นจุดตัดของคีย์พจนานุกรมและตัวกรองของคุณ:

keys = cells.keys() & keys

โดยสรุป: delได้รับการปรับให้เหมาะสมอย่างมากแล้วดังนั้นอย่ากังวลกับการใช้งาน


-1

ฉันมาสายสำหรับการสนทนานี้ แต่สำหรับคนอื่น วิธีแก้ปัญหาคือการสร้างรายการคีย์ดังกล่าว

k = ['a','b','c','d']

จากนั้นใช้ pop () ในการทำความเข้าใจรายการหรือสำหรับการวนซ้ำเพื่อวนซ้ำบนปุ่มและป๊อปทีละรายการเช่นนี้

new_dictionary = [dictionary.pop(x, 'n/a') for x in k]

'n / a' ในกรณีที่ไม่มีคีย์ต้องส่งคืนค่าเริ่มต้น


8
new_dictionaryดูแย่มากเหมือนรายการ;)
DylanYoung
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.