ตรวจสอบว่าคีย์ที่กำหนดมีอยู่แล้วในพจนานุกรมและเพิ่มขึ้นหรือไม่


294

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

คือฉันต้องการทำสิ่งนี้:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

เช่นฉันต้องการเพิ่มมูลค่าหากมีอยู่แล้วหรือตั้งเป็น 1


11
โค้ดขนาดเล็ก nitpick: รหัสจะตั้งค่า my_dict [key] เป็น 1 หากมีบางอย่างอยู่ที่นั่นและจะเพิ่มขึ้นหากไม่มี ฉันคิดว่าคุณต้องการ == ไม่ใช่! =
QuantumFool

คำตอบ:


331

คุณกำลังมองหาcollections.defaultdict(สำหรับ Python 2.5+) นี้

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

จะทำในสิ่งที่คุณต้องการ

สำหรับ Python dictทั่วไปหากไม่มีค่าสำหรับคีย์ที่ระบุคุณจะไม่ได้รับNoneเมื่อเข้าถึง dict - a KeyErrorจะถูกยกขึ้น ดังนั้นหากคุณต้องการใช้งานแบบปกติdictแทนที่จะใช้รหัสของคุณคุณก็จะใช้

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

8
ตามตัวอย่างของเขามันควรจะเพียงพอที่จะตั้ง "defaultdict (แลมบ์ดา: 0)" และข้ามประโยค "ถ้า" ทั้งหมด
Deestan

ใช้งานได้ แต่สร้างความสับสนให้กับคีย์และค่า (ทำให้อ่านค่อนข้างแปลก) 'some_value' ควรเป็น 'some_key'
mikemaccana

@ thumbnailer: แก้ไขแล้วขอบคุณ ตอนแรกฉันใช้ 'some_value' เพราะนั่นคือชื่อตัวแปรในคำถาม แต่ฉันยอมรับว่ามันชัดเจนขึ้นแล้ว
dF

20
... หรือปกติของคุณสามารถทำdict my_dict[key] = my_dict.get(key, 0) + 1
minmaxavg

จะขยายให้เป็นพจนานุกรมที่ซ้อนกันได้อย่างไร dict [key1] [key2] + = 1?
Pablo Ruiz Ruiz

300

ฉันชอบที่จะทำในรหัสบรรทัดเดียว

my_dict = {}

my_dict [some_key] = my_dict.get (some_key, 0) + 1

พจนานุกรมมีฟังก์ชันรับซึ่งใช้พารามิเตอร์สองตัว - รหัสที่คุณต้องการและค่าเริ่มต้นหากไม่มีอยู่ ฉันชอบวิธีนี้เป็นค่าเริ่มต้นตามที่คุณต้องการจัดการกรณีที่ไม่มีคีย์ในรหัสบรรทัดนี้ไม่ใช่ทุกที่


1
@AndrewWilkinson ฉันไม่ดี ไม่ได้อ่านคำตอบของคุณอย่างละเอียดเท่าที่ควร
masaers

59

โดยส่วนตัวฉันชอบใช้ setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

setdefaultมันยอดเยี่ยม some_keyมันไม่ได้เปลี่ยนแปลงค่าหากมีการกำหนดไว้แล้วสำหรับ ยกตัวอย่างเช่นไม่รบกวนค่าของd={1:2}; d.setdefault(1, 0) d[1]
wsaleem

49

คุณต้องการkey in dictสำนวนที่

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

อย่างไรก็ตามคุณควรพิจารณาใช้defaultdict(ตามที่ dF แนะนำ)


1
โปรดทราบว่าอย่างน้อย 2.6 has_key () ได้รับการสนับสนุนจากคีย์ใน d ฉันคิดว่ามันเป็นแบบนี้ใน 2.5 เช่นกัน
David Locke

โปรดทราบว่าหนึ่งสามารถเขียนmy_dict[key] is not Noneซึ่งเป็นที่ชัดเจน (อย่างน้อย IMHO)
brandizzi

@brandizzi - เห็นด้วยif key in my_dict and my_dict[key]:
Rob Grant

18

ในการตอบคำถาม " ฉันจะทราบได้อย่างไรว่าดัชนีที่กำหนดใน dict นั้นถูกตั้งค่าเป็นค่าที่ไม่ใช่ None " หรือไม่ฉันต้องการสิ่งนี้:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

สิ่งนี้สอดคล้องกับแนวคิดที่ถูกเรียกใช้ของ EAFP (ง่ายต่อการขอการให้อภัยและได้รับอนุญาต) นอกจากนี้ยังหลีกเลี่ยงการค้นหาคีย์ที่ซ้ำกันในพจนานุกรมเช่นเดียวกับในkey in my_dict and my_dict[key] is not Noneสิ่งที่น่าสนใจหากการค้นหามีราคาแพง

สำหรับปัญหาจริงที่คุณโพสต์ไว้นั่นคือการเพิ่ม int ถ้ามีอยู่หรือตั้งค่าเป็นค่าเริ่มต้นมิฉะนั้นฉันขอแนะนำ

my_dict[key] = my_dict.get(key, default) + 1

ดังคำตอบของ Andrew Wilkinson

มีวิธีที่สามถ้าคุณเก็บวัตถุที่แก้ไขได้ในพจนานุกรมของคุณ ตัวอย่างทั่วไปสำหรับสิ่งนี้คือmultimapที่คุณเก็บรายการองค์ประกอบสำหรับคีย์ของคุณ ในกรณีนั้นคุณสามารถใช้:

my_dict.setdefault(key, []).append(item)

หากไม่มีค่าสำหรับคีย์ในพจนานุกรมเมธอด setdefault จะตั้งเป็นพารามิเตอร์ตัวที่สองของ setdefault มันจะทำงานเหมือน my_dict มาตรฐาน [กุญแจ] คืนค่าสำหรับคีย์ (ซึ่งอาจเป็นค่าที่ตั้งใหม่)


สิ่งที่ดูเหมือนจริง Pythonic (เพื่อคนนอกเหมือนตัวเอง) คือคำถามใด ๆ มีอย่างน้อย 3 คำตอบที่ถูกต้อง :)
davka

@davka: กรณีการใช้สามกรณีเกือบเหมือนกัน แต่แตกต่างกัน: a) ค้นหาว่ามีองค์ประกอบที่ไม่มีในพจนานุกรม b หรือไม่) เรียกคืนค่าจากพจนานุกรมหรือใช้ค่าเริ่มต้นหากไม่มีค่า c) ดึงค่าจากพจนานุกรมและจัดเก็บค่าเริ่มต้นหากยังไม่มีค่า
ครั้งที่

ฉันรู้ว่า :) นี่ไม่ใช่คำวิจารณ์ฉันสนุกกับความจริงข้อนี้
davka

ในความคิดเห็นต่อคำตอบของ @ ryeguy สจวร์ตวู้ดเวิร์ดแนะนำ "ค่าใช้จ่ายในการจัดการข้อยกเว้นในภาษามักจะมีลำดับความสำคัญมากกว่าการค้นหาตารางแฮชที่กำหนดว่ารายการนั้นมีอยู่หรือไม่อยู่ในพจนานุกรม" ในขณะที่คุณพูดว่า หลีกเลี่ยงการค้นหาคีย์ซ้ำในพจนานุกรม ... หากการค้นหามีราคาแพง "- ไม่มีใครมีการวัดว่าการจัดการข้อยกเว้นเร็วกว่าหรือช้ากว่าการค้นหาคีย์คู่หรือไม่
Michael Firth

1
@MichaelFirth ฉันค้นหาคร่าวๆของ Python: stackoverflow.com/questions/2522005/…มันช้ากว่า แต่ก็ไม่มาก โปรดทราบว่าแนวคิดการขว้างปาข้อยกเว้นระดับสูงนั้นมีการจัดการแตกต่างกันมากในภาษาต่างๆและคุณไม่สามารถพูดถึงข้อดีข้อเสีย ดังนั้นในขณะที่ "ข้อยกเว้นมีค่าใช้จ่าย 10x" อาจถูกต้องสำหรับ Java แต่ไม่ใช่สำหรับ Python (หรือ Swift หรืออื่น ๆ )
ครั้งที่

13

เห็นด้วยกับ cgoldberg ฉันจะทำอย่างไร:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

ดังนั้นทำตามข้างบนหรือใช้ dict ปริยายตามที่คนอื่นแนะนำ อย่าใช้ถ้าคำสั่ง นั่นไม่ใช่ Pythonic


8
ถ้างบไม่ Pythonic อย่างไร
Adam Parkin

2
ฉันคิดว่านี่เป็นกรณีเดียวที่ EAFP ของ Python ไม่ใช่วิธีที่ดีที่สุด ตัวอย่างของคุณด้านบนมีรหัสซ้ำ สิ่งที่ถ้าวันหนึ่งเราต้องการ+=2หรือ-=1? คุณต้องจำไว้ว่าให้เปลี่ยนทั้งสองบรรทัด อาจดูเหมือนเป็นเรื่องเล็กน้อยในตอนนี้ แต่สิ่งเหล่านี้เป็นข้อบกพร่องเล็ก ๆ น้อย ๆ ที่โง่เง่าที่สามารถกลับมากัดคุณได้
Cam Jackson

3
สิ่งนี้ดูดีและทำงานได้ดี แต่ฉันมักจะหลีกเลี่ยงการทำเช่นนี้เพราะฉันคิดว่าค่าใช้จ่ายในการจัดการข้อยกเว้นในภาษามักจะมีลำดับความสำคัญมากกว่าการค้นหาตารางแฮชที่กำหนดว่ารายการนั้นมีอยู่ในพจนานุกรมหรือไม่
Stuart Woodward

11

อย่างที่คุณเห็นจากคำตอบมากมายมีหลายวิธีแก้ไข ตัวอย่างหนึ่งของ LBYL (ดูก่อนที่คุณจะกระโดด) ยังไม่ได้กล่าวถึงวิธีการ has_key ():

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

6
has_key () ช้ากว่าโอเปอเรเตอร์ 'in' และอ่านได้น้อยกว่า
Abgan

9
... และเลิกใช้แล้วใน Python 2.6 และถูกลบออกใน Python 3
Tim Pietzcker

7

วิธีที่คุณพยายามทำนั้นเรียกว่า LBYL (ดูก่อนที่คุณจะกระโดด) เนื่องจากคุณกำลังตรวจสอบเงื่อนไขก่อนที่จะพยายามเพิ่มมูลค่าของคุณ

อีกวิธีหนึ่งเรียกว่า EAFP (ง่ายต่อการขอการให้อภัยและอนุญาต) ในกรณีนั้นคุณจะลองดำเนินการ (เพิ่มค่า) หากล้มเหลวคุณจะตรวจพบข้อยกเว้นและตั้งค่าเป็น 1 นี่เป็นวิธี Pythonic ที่น่าสนใจมากขึ้น (IMO)

http://mail.python.org/pipermail/python-list/2003-May/205182.html


5

สายไปหน่อย แต่สิ่งนี้น่าจะใช้ได้

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

ว้าวในฐานะโปรแกรมเมอร์ Java นี่เป็นโครงสร้างที่ดูเพี้ยน ดูเหมือนว่าผู้ประกอบการที่สั่งซื้อแปลกประหลาด?
forresthopkinsa

5

นี้ไม่ได้โดยตรงตอบคำถาม แต่กับผมมันดูเหมือนว่าคุณอาจต้องการการทำงานของcollections.Counter

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

ผลลัพธ์นี้ในผลลัพธ์

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

ตัวนับทำงานเหมือนกับdefaultdict(int)ฟังก์ชันพิเศษบางอย่างดังนั้นมันจะทำงานได้อย่างสมบูรณ์แบบเมื่อจัดการกับจำนวนเต็มโดยเฉพาะ แต่คุณไม่แสดงพฤติกรรมที่เกี่ยวข้องใด ๆ
Tadhg McDonald-Jensen

4

ต่อไปนี้เป็นหนึ่งในสายการบินที่ฉันได้พบเมื่อเร็ว ๆ นี้เพื่อแก้ไขปัญหานี้ มันขึ้นอยู่กับวิธีการพจนานุกรมsetdefault :

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1

0

ฉันกำลังมองหามันไม่พบในเว็บจากนั้นลองเสี่ยงโชคของฉันกับ Try / Error และพบว่า

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

1
คุณไม่ควรใช้__contains__รหัสการผลิต BTW เป็นเช่นเดียวกับการใช้__contains__ is
user1767754

1
my_dict.__contains__(some_key)มีค่าเท่ากับsome_key in my_dict, เกินพิกัดสำหรับinตัวดำเนินการที่ไม่ใช่is
Tadhg McDonald-Jensen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.