anahtarların belirli bir dizeyi içerdiği bir python sözlüğündeki öğeleri filtreleyin


97

Python'da bir şeyler geliştiren bir C kodlayıcıyım Aşağıdakileri C'de (ve dolayısıyla python'a uygulanan C benzeri mantıkta) nasıl yapacağımı biliyorum, ancak bunu yapmanın 'Python' yolunun ne olduğunu merak ediyorum.

Bir sözlüğüm var d ve öğelerin bir alt kümesi üzerinde çalışmak istiyorum, yalnızca anahtarı (dizesi) belirli bir alt dizeyi içerenler.

yani C mantığı şöyle olacaktır:

for key in d:
    if filter_string in key:
        # do something
    else
        # do nothing, continue

Python versiyonunun şöyle bir şey olacağını hayal ediyorum

filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
    # do something

Burada sözlükleri filtrelemeyle ilgili çok sayıda gönderi buldum, ancak tam olarak bununla ilgili bir tane bulamadım.

Sözlüğüm iç içe değil ve python 2.7 kullanıyorum



Yanıtlar:


187

Bir dikte anlamaya ne dersiniz :

filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}

Gördüğünüz gibi, oldukça iyi İngilizce okuduğu için kendi kendini açıklayıcı olmalı.

Bu sözdizimi Python 2.7 veya üzerini gerektirir.

Sadece Python 3, orada dict.items()değil, iteritems()kullandığınız diye:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}

1
Neden olmasın filtered_dict = {k:d[k] for k in d if filter_string in k}?
thefourtheye

5
@thefourtheye Arama sırasında olmadığı için benimkinin daha hızlı olduğunu tahmin edeceğim d[k].
Jonathon Reinhart

Ayrıca # do somethingyorumlarda diyor ama burada birkaç anahtar bırakıyoruz.
thefourtheye

Biz var mı iteritemsPython 3'te? Ben öyle düşünmüyorum. Yani benim versiyonum uyumlu olacak, değil mi?
thefourtheye

1
Python 3'te , Python 2.7'ler ile aynı olan iteritemsile değiştirirsiniz . itemsiteritems
Jonathon Reinhart

18

En okunaklı ve bakımı kolay olanı seçin. Sırf tek satırda yazabilmen, yazman gerektiği anlamına gelmez. Mevcut çözümünüz, değer aramasını atlamak için kullanacağım yinelemelerden başka kullanacağım çözümlere yakın ve bunlardan kaçınabiliyorsam iç içe geçmiş if'lerden nefret ediyorum:

for key, val in d.iteritems():
    if filter_string not in key:
        continue
    # do something

Bununla birlikte, gerçekten bir şeyin filtrelenmiş bir dikteyi yinelemenize izin vermesini istiyorsanız, o zaman filtrelenmiş bir dikteyi oluşturmak ve sonra onu yinelemek için iki aşamalı işlemi yapmam, bunun yerine bir jeneratör kullanmam, çünkü bundan daha pitonik (ve harika) olan şey bir jeneratör?

Önce jeneratörümüzü yaratırız ve iyi tasarım onu ​​yeniden kullanılabilir olacak kadar soyut yapmamızı gerektirir:

# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
    for key, val in d.iteritems():
        if filter_string not in key:
            continue
        yield key, val

Ardından, probleminizi basit, anlaşılır kodla güzel ve temiz bir şekilde çözmek için jeneratörü kullanabiliriz:

for key, val in filter_dict(d, some_string):
    # do something

Kısacası: jeneratörler harika.


11

Sözlükleri, listeleri vb. Belirli koşullara göre filtrelemek için yerleşik filtre işlevini kullanabilirsiniz.

filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))

Avantajı, farklı veri yapıları için kullanabilmenizdir.


Bunun lambda tanımında items:olması gerektiğini unutmayın item:.
bkribbs

Hatayı işaret ettiğiniz için @bkribbs'e teşekkür ederiz. Şimdi düzelttim.
Pulkit

8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}

3
Benim yöntemimden iteritems()daha verimli olacak items().
Jonathon Reinhart

@Jonathin Reinhart bunu bilmiyordum. Teşekkürler.
jspurim

2
Yalnızca Python 2.7'de. Python 3'te yalnızca items() Python 2.7'ler gibi davranan vardır iteritems.
Jonathon Reinhart

1
Soru açıkça python 2.7 için
Brendan F

7

Jonathon , cevabında dikte anlamalarını kullanan bir yaklaşım verdi . İşte bir şeyler yapmanla ilgilenen bir yaklaşım .

Sözlüğün değerleri ile bir şeyler yapmak istiyorsanız, bir sözlük anlamasına ihtiyacınız yok:

iteritems() Sorunuzu etiketlediğinizden beri kullanıyorum

results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])

Şimdi sonuç some_function, sözlüğün anahtarında bulunan her anahtar / değer çiftine uygulanan bir listede olacaktır foo.

Yalnızca değerlerle uğraşmak ve anahtarları görmezden gelmek istiyorsanız, listenin anlaşılmasını değiştirin:

results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])

some_function herhangi bir çağrılabilir olabilir, bu nedenle bir lambda da çalışır:

results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])

Eşlemek için bir üretici ifadesini de iletebileceğinizden, iç liste aslında gerekli değildir :

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]

ilginç. some_function nasıl tanımlanır? ilk durumda (k, v), sadece iki parametre mi alıyor? ilk anahtar sonra değer?
notu

Evet, sadece aranabilir. Yani map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))- bu size verecek [4].
Burhan Khalid

Bu doğrudur, ancak kullanmaktan daha fazla pitonik mapbir liste anlamadır. [f(v) for k, v in d.iteritems() if substring in k]Bence çok daha okunaklı ve daha verimli.
Davidmh

@memo İki parametre almaz, iki elemanlı tek bir parametre alır. Ayrıca, iki argümana ayrılacak bir starmap vardır, ancak bu tembel bir yineleyicidir (çalıştırılmadan önce yinelenmelidir, yani results = list(starmap(...))veya for result in starmap(...): ...).
nmclean
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.