Periksa apakah kunci yang diberikan sudah ada dalam kamus dan tambahkan itu

295

Diberikan kamus, bagaimana saya bisa mengetahui jika kunci yang diberikan dalam kamus itu telah disetel ke nilai bukan-Tidak ada?

Yaitu, saya ingin melakukan ini:

my_dict = {}

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

Yaitu, saya ingin menambah nilainya jika sudah ada di sana, atau setel ke 1 sebaliknya.

Ben
sumber
11
Nitpick kode kecil: kode ini menetapkan my_dict [kunci] menjadi 1 jika sudah ada sesuatu di sana, dan menambahkannya jika tidak ada. Saya pikir Anda ingin ==, bukan! =.
QuantumFool

Jawaban:

331

Anda mencari collections.defaultdict(tersedia untuk Python 2.5+). Ini

from collections import defaultdict

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

akan melakukan apa yang Anda inginkan.

Untuk Python dicts reguler , jika tidak ada nilai untuk kunci yang diberikan, Anda tidak akan mendapatkan Noneketika mengakses dict - a KeyErrorakan dinaikkan. Jadi jika Anda ingin menggunakan yang biasa dict, alih-alih kode Anda yang akan Anda gunakan

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1
dF.
sumber
8
Menurut contohnya, itu harus cukup untuk menetapkan "defaultdict (lambda: 0)" dan melewatkan seluruh klausa "jika".
Deestan
Ini berfungsi, tetapi membingungkan kunci dan nilai (membuatnya agak aneh dibaca). 'some_value' harus 'some_key'
mikemaccana
@ thumbnailer: diperbaiki, terima kasih. Saya awalnya menggunakan 'some_value' karena itulah nama variabel dalam pertanyaan, tapi saya setuju itu lebih jelas sekarang.
dF.
20
... atau untuk yang biasa dict, bisa Anda lakukan my_dict[key] = my_dict.get(key, 0) + 1.
minmaxavg
Bagaimana cara memperpanjang ini ke kamus bersarang? dict [key1] [key2] + = 1?
Pablo Ruiz Ruiz
301

Saya lebih suka melakukan ini dalam satu baris kode.

my_dict = {}

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

Kamus memiliki fungsi, dapatkan, yang mengambil dua parameter - kunci yang Anda inginkan, dan nilai default jika tidak ada. Saya lebih suka metode ini daripada defaultdict karena Anda hanya ingin menangani kasus di mana kunci tidak ada dalam satu baris kode ini, tidak di mana-mana.

Andrew Wilkinson
sumber
1
@AndrewWilkinson salahku. Tidak membaca jawaban Anda selengkap yang seharusnya.
masaers
59

Saya pribadi suka menggunakan setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1
kichik
sumber
setdefaultMengagumkan. Itu tidak mengubah nilai jika sudah ditetapkan untuk some_key. Misalnya, d={1:2}; d.setdefault(1, 0)tidak mengganggu nilai d[1].
wsaleem
49

Anda butuh key in dictidiom untuk itu.

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

Namun, Anda mungkin harus mempertimbangkan untuk menggunakan defaultdict(seperti yang disarankan dF).

Eli Bendersky
sumber
1
Harap dicatat bahwa setidaknya 2,6 has_key () telah dihapus demi kunci dalam d. Saya pikir begini dalam 2,5 juga.
David Locke
Perhatikan bahwa orang dapat menulis my_dict[key] is not None, yang lebih jelas (setidaknya IMHO)
brandizzi
@brandizzi - setuju,if key in my_dict and my_dict[key]:
Rob Grant
18

Untuk menjawab pertanyaan " bagaimana saya bisa mengetahui jika indeks yang diberikan dalam dikt itu telah ditetapkan ke nilai yang tidak ada ", saya lebih suka ini:

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

Ini sesuai dengan konsep EAFP yang sudah dipanggil (lebih mudah untuk meminta maaf daripada izin). Ini juga menghindari pencarian kunci duplikat dalam kamus karena itu akan key in my_dict and my_dict[key] is not Nonemenarik jika pencarian mahal.

Untuk masalah aktual yang Anda ajukan, yaitu menambah int jika ada, atau mengaturnya ke nilai default jika tidak, saya juga merekomendasikan

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

seperti dalam jawaban Andrew Wilkinson.

Ada solusi ketiga jika Anda menyimpan objek yang dapat dimodifikasi di kamus Anda. Contoh umum untuk ini adalah multimap , di mana Anda menyimpan daftar elemen untuk kunci Anda. Dalam hal ini, Anda dapat menggunakan:

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

Jika nilai untuk kunci tidak ada dalam kamus, metode setdefault akan mengaturnya ke parameter kedua setdefault. Berperilaku seperti standar my_dict [kunci], mengembalikan nilai untuk kunci (yang mungkin merupakan nilai yang baru ditetapkan).

nd.
sumber
Yang terlihat memang Pythonic (untuk orang luar seperti saya) adalah bahwa setiap pertanyaan memiliki setidaknya 3 jawaban yang valid :)
davka
@davka: Ya, ketiga kasus penggunaan hampir sama, tetapi berbeda: a) mencari tahu apakah ada elemen non-Tidak ada dalam kamus b) mengambil nilai dari kamus atau menggunakan default jika nilainya tidak ada c) mengambil nilai dari kamus dan menyimpan default jika nilai belum ada.
nd.
Saya tahu :) ini bukan kritik, saya hanya terhibur dengan fakta ini
davka
Dalam komentar ke jawaban @ ryeguy, Stuart Woodward menyarankan "overhead dalam penanganan Exception dalam bahasa selalu urutan besarnya lebih besar daripada pencarian tabel hash yang menentukan apakah item ada atau tidak dalam kamus", sementara Anda mengatakan "Ini juga menghindari pencarian kunci duplikat di kamus ... jika pencarian mahal "- apakah ada yang punya pengukuran di mana penanganan pengecualian lebih cepat atau lebih lambat daripada pencarian kunci ganda?
Michael Firth
1
@MichaelFirth Saya melakukan pencarian sepintas untuk pengecualian overhead Python: stackoverflow.com/questions/2522005/... lebih lambat, tetapi tidak banyak. Ingatlah bahwa konsep melempar Pengecualian tingkat tinggi ditangani dengan sangat berbeda dalam berbagai bahasa dan Anda tidak dapat menggeneralisasikan pro dan kontra. Jadi sementara "Pengecualian memiliki overhead 10x" mungkin benar untuk Java, itu bukan untuk Python (atau Swift atau yang lain).
nd.
13

Setuju dengan cgoldberg. Bagaimana saya melakukannya adalah:

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

Jadi lakukan seperti di atas, atau gunakan dict default seperti yang disarankan orang lain. Jangan gunakan pernyataan if. Itu bukan Pythonic.

ryeguy
sumber
8
Bagaimana jika pernyataan bukan Pythonic?
Adam Parkin
2
Saya pikir ini adalah satu kasus di mana EAFP Python bukan cara terbaik. Contoh Anda di atas memiliki kode duplikat; bagaimana jika suatu hari kita mau +=2atau -=1? Anda harus ingat untuk mengubah kedua baris. Ini mungkin tampak seperti hal yang sepele sekarang, tetapi itu adalah jenis bug 'sepele' kecil bodoh yang dapat kembali menggigit Anda.
Cam Jackson
3
Ini terlihat bagus dan berfungsi dengan baik, tetapi saya biasanya menghindari melakukannya seperti ini karena saya berpikir bahwa overhead dalam penanganan Exception dalam bahasa selalu urutan besarnya lebih besar daripada pencarian tabel hash yang menentukan apakah item ada atau tidak dalam kamus.
Stuart Woodward
11

Seperti yang dapat Anda lihat dari banyak jawaban, ada beberapa solusi. Satu contoh LBYL (lihat sebelum Anda melompat) belum disebutkan, metode 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
bortzmeyer
sumber
6
has_key () lebih lambat dari operator 'in' dan kurang dapat dibaca.
Abgan
9
... dan telah ditinggalkan dalam Python 2.6 dan dihapus dalam Python 3.
Tim Pietzcker
7

Cara Anda mencoba melakukannya disebut LBYL (lihat sebelum Anda melompat), karena Anda memeriksa kondisi sebelum mencoba meningkatkan nilai Anda.

Pendekatan lain disebut EAFP (lebih mudah untuk meminta pengampunan daripada izin). Dalam hal ini, Anda hanya akan mencoba operasi (menambah nilainya). Jika gagal, Anda menangkap pengecualian dan menetapkan nilai ke 1. Ini adalah cara yang sedikit lebih Pythonic untuk melakukannya (IMO).

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

Corey Goldberg
sumber
5

Agak terlambat tapi ini seharusnya berhasil.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1
Bob
sumber
Wow, sebagai programmer Java, ini adalah konstruksi yang cukup gila. Sepertinya operator ternary yang dipesan secara aneh?
forresthopkinsa
5

Ini tidak langsung menjawab pertanyaan, tetapi bagi saya, sepertinya Anda mungkin menginginkan fungsionalitas koleksi . Penghitung .

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

Ini menghasilkan output

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
Izaak van Dongen
sumber
Counter berfungsi seperti halnya defaultdict(int)dengan beberapa fungsionalitas tambahan sehingga akan bekerja dengan sempurna ketika berhadapan secara eksklusif dengan bilangan bulat tetapi Anda tidak menunjukkan perilaku yang relevan.
Tadhg McDonald-Jensen
4

Inilah satu-liner yang saya buat baru-baru ini untuk menyelesaikan masalah ini. Ini didasarkan pada metode kamus setdefault :

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

Saya mencarinya, tidak menemukannya di web lalu mencoba keberuntungan saya dengan Try / Error dan menemukannya

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1
AbhishekKr
sumber
1
Anda seharusnya tidak menggunakan __contains__kode produksi. btw. __contains__sama dengan menggunakan is.
user1767754
1
my_dict.__contains__(some_key)setara dengan some_key in my_dict, kelebihan beban untuk inoperator tidakis
Tadhg McDonald-Jensen