Listede neden sözlük gibi güvenli “get” yöntemi bulunmuyor?


264

Listede neden sözlük gibi güvenli bir "get" yöntemi yok?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range

1
Listeler sözlüklerden farklı amaçlar için kullanılır. Listenin tipik kullanım durumları için get () gerekmez. Ancak sözlük için get () genellikle yararlıdır.
mgronber

42
Dilim yerine şunu sorarsanız, IndexError değerini yükseltmeden listeden boş bir alt liste alabilirsiniz . Örneğin, l[10:11]yerine l[10]. () Bu alt
liste

56
Bazılarının aksine, kasa fikrini destekliyorum .get. Eşdeğer l[i] if i < len(l) else default, ancak daha okunabilir, daha özlü iolacak ve yeniden hesaplamak zorunda kalmadan bir ifade olmasına izin verecek
Paul Draper

6
Bugün bunun var olmasını diledim. Bir liste döndüren pahalı bir işlev kullanıyorum, ancak yalnızca ilk öğeyi istedim ya Noneda yoksa. x = expensive().get(0, None)Pahalı işe yaramaz bir dönüşü geçici bir değişkene koymak zorunda kalmayacağımı söylemek güzel olurdu.
Ryan Hiebert

2
@ Cevabım cevabınız size yardımcı olabilir stackoverflow.com/a/23003811/246265
Jake

Yanıtlar:


112

Sonuçta muhtemelen güvenli bir .getyöntemi yoktur, çünkü a dict, bir anahtarın istisna atmadan var olup olmadığını kontrol etmenin (ve değerini döndürmesinin) süper önemsiz olduğu durumlarda ilişkisel bir koleksiyon (değerler adlarla ilişkilidir). Liste öğelerine erişen istisnalardan kaçınmak için ( lenyöntem çok hızlı olduğu için). .getYöntem doğrudan (eğer listenizin soruyorsun fazla gibi olurdu) sözlükte 37 öğeye erişmek değil, bir adla ilişkilendirilmiş değeri sorgulamak için izin verir.

Tabii ki, bunu kendiniz kolayca uygulayabilirsiniz:

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

Hatta üzerine monkeypatch olabilir __builtins__.listiçinde yapıcı __main__, ancak çoğu kod kullanmak olmadığından bir az yaygın bir değişim olacaktır. Bunu sadece kendi kodunuz tarafından oluşturulan listelerle kullanmak istiyorsanız, sadece alt sınıfı sınıflandırabilir listve getyöntemi ekleyebilirsiniz .


24
Python maymunsu gibi yerleşik tiplere izin vermezlist
Imran

7
@ CSZ: Listede bulunmayan .getbir sorunu çözer - var olmayan veriler alınırken istisnaları önlemenin etkili bir yolu. Geçerli bir liste dizininin ne olduğunu bilmek çok önemsiz ve çok etkilidir, ancak sözlükteki anahtar değerler için bunu yapmanın özellikle iyi bir yolu yoktur.
Nick Bastin

10
Bunun verimlilikle ilgili olduğunu hiç sanmıyorum - bir anahtarın sözlükte mevcut olup olmadığını kontrol etmek ve / veya bir öğeyi döndürmek O(1). Ham anlamda kontrol etmek kadar hızlı olmayacak len, ancak karmaşıklık açısından hepsi bu O(1). Doğru cevap tipik kullanım / semantik bir ...
Mark Longair

3
@ Mark: Tüm O (1) eşit yaratılmaz. Ayrıca, dicttüm vakalar değil, sadece en iyi durum O (1) 'dir.
Nick Bastin

4
Bence insanlar buradaki noktayı kaçırıyorlar. Tartışma verimlilikle ilgili olmamalıdır. Lütfen erken optimizasyonla durdurun. Programınız çok yavaşsa, kötüye kullanıyorsunuz .get()veya kodunuzun (veya ortamınızın) başka bir yerinde sorunlarınız var. Böyle bir yöntemi kullanmanın amacı kod okunabilirliğidir. "Vanilya" tekniği, bunun yapılması gereken her yerde dört satır kod gerektirir. .get()Bu teknik sadece bir gerektirir ve kolayca takip eden yöntem çağrıları (örneğin zincirleme edilebilir my_list.get(2, '').uppercase()).
Tyler Crompton

67

Bu, ilk öğeyi istiyorsanız, my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

Tam olarak istediğin şey olmadığını biliyorum ama başkalarına yardımcı olabilir.


7
daha az işlevsel programlama kıvamındaki daha pythonic
Eric

next(iter(my_list[index:index+1]), 'fail')Sadece 0 değil, daha az FP değil, tartışmasız daha fazla Pythonic ve neredeyse kesinlikle daha okunabilir herhangi bir indeks sağlar my_list[index] if index < len(my_list) else 'fail'.
alphabetasoup

47

Muhtemelen liste anlambilimi için pek bir anlam ifade etmediği için. Ancak, alt sınıflama ile kendinizinkini kolayca oluşturabilirsiniz.

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()

5
Bu, OP'ye cevap veren en pitoniktir. Python'da güvenli bir işlem olan bir alt listeyi de çıkarabileceğinizi unutmayın. Mylist = [1, 2, 3] verildiğinde, istisnayı tetiklemeden 9. elemanı mylist [8: 9] ile çıkarmayı deneyebilirsiniz. Daha sonra listenin boş olup olmadığını test edebilir ve boş değilse tek öğeyi döndürülen listeden ayıklayabilirsiniz.
jose.angel.jimenez

1
Bu kabul edilen cevap olmalı, diğer pythonic olmayan tek katmanlı korsanları değil, özellikle de sözlüklerle simetriyi koruduğu için.
Eric

1
Sadece güzel bir getyönteme ihtiyaç duyduğunuz için kendi listelerinizi alt sınıflandırma konusunda pitonik bir şey yoktur . Okunabilirlik önemlidir. Ve okunabilirlik her gereksiz sınıfta acı çekiyor. Sadece try / exceptalt sınıflar oluşturmadan yaklaşımı kullanın .
Jeyekomon

@Jeyekomon Alt plakayı kullanarak kazan plakasını azaltmak mükemmel bir Pythonic.
Keith

42

.Get kullanmak yerine, böyle kullanmak listeler için uygun olmalıdır. Sadece bir kullanım farkı.

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

15
-1 ile en son elemanı almaya çalışırsak bu başarısız olur.
pretobomba

Bunun dairesel bağlı liste nesneleri için çalışmadığını unutmayın. Ayrıca, sözdizimi bir "tarama bloğu" olarak adlandırmak istediğim neden olur. Ne yaptığını görmek için kodu tararken, bu beni bir an için yavaşlatan bir satır.
Tyler Crompton

inline, 2.6 gibi eski python ile çalışmıyorsa (veya 2.5 mi?)
Eric

3
@TylerCrompton: Python'da dairesel bağlantılı bir liste yok. Kendi başınıza bir tane yazdıysanız, bir .getyöntem uygulayabilirsiniz (ancak bu durumda dizinin ne anlama geldiğini veya neden başarısız olacağını açıklayamıyorum).
Nick Bastin

Sınır dışı negatif endeksleri ele alan bir alternatif olacaktırlst[i] if -len(lst) <= i < len(l) else 'fail'
mic

17

Bunu dene:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

1
ilk olarak yeni bir alt liste oluşturulmasını gerektirir, ancak ben bu bir gibi.
Rick, Monica

15

İçin Kredi jose.angel.jimenez


"Oneliner" hayranları için…


Bir listenin ilk öğesini istiyorsanız veya liste boşsa varsayılan bir değer istiyorsanız şunu deneyin:

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

İadeler a

ve

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

İadeler default


Diğer öğeler için örnekler…

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']

Varsayılan yedekle…

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c

İle test edildi Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)


1
Kısa bir alternatif: value, = liste[:1] or ('default',). Parantezlere ihtiyacınız var gibi görünüyor.
qräbnö

13

Yapabileceğiniz en iyi şey, listeyi bir diktüre dönüştürmek ve ardından get yöntemiyle erişmektir:

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')

20
Makul bir çözüm, ancak neredeyse "yapabileceğiniz en iyi şey".
üçlü

3
Yine de çok verimsiz. Not: Bu zip range lenşey yerine, sadece kullanabilirsinizdict(enumerate(my_list))
Marian

3
Bu en iyi şey değil, yapabileceğiniz en kötü şey.
erikbwork

3
Performansı düşünürseniz en kötü şey ... Performansı önemsiyorsanız, python gibi yorumlanmış bir dilde kod yazmazsınız. Bu çözümü oldukça zarif, güçlü ve pitonik bir sözlük kullanarak buluyorum. Erken optimizasyonlar zaten kötüydü, bu yüzden bir karar verelim ve daha sonra bunun bir darboğaz olduğunu görelim.
Eric

7

Bunun üzerine biraz daha araştırma yaptım ve bunun için özel bir şey olmadığı ortaya çıktı. List.index (değer) bulduğumda heyecanlandım, belirtilen bir öğenin dizinini döndürür, ancak belirli bir dizinde değeri almak için hiçbir şey yoktur. Bu yüzden oldukça iyi olduğunu düşünüyorum safe_list_get çözümünü kullanmak istemiyorsanız. Senaryoya bağlı olarak işi sizin için halledebilecek bazı 1 liner if ifadeleri şunlardır:

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

Daha mantıklı olan 'Hayır' yerine Yok'u da kullanabilirsiniz:

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

Ayrıca listedeki ilk veya son öğeyi almak istiyorsanız, bu işe yarar

end_el = x[-1] if x else None

Bunları işlevlere de ekleyebilirsiniz, ancak hala IndexError istisna çözümünü beğendim. safe_list_getÇözümün aptal bir versiyonunu denedim ve biraz daha basit hale getirdim (varsayılan yok):

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

En hızlı olanı görmek için kıyaslama yapmadım.


1
Gerçekten pitonik değil.
Eric

@ Hangi kod parçacığı? Bence denemek, yine ona bakmak en mantıklı.
radtek

Bağımsız bir işlev pitonik değildir. İstisnalar gerçekten biraz daha pitoniktir, ancak programlama dillerinde yaygın bir kalıp olduğu kadar değil. Dahası pitonik, yerleşik türü listalt sınıflandırarak genişleten yeni bir nesnedir . Bu şekilde kurucu listbir liste gibi davranan bir şey alabilir veya yeni örnek tıpkı a list. Aşağıdaki kabul edilen IMHO olması gereken Keith'in cevabına bakınız.
Eric

1
@Eric Soruyu OOP'ye özgü olarak ayrıştırmadım ama "listelerin neden dict.get()yakalamak yerine bir liste dizini başvurusundan varsayılan bir değer döndürmek için bir benzeşmesi yok IndexError? Yani gerçekten dil / kütüphane özelliği ile ilgili Dahası, muhtemelen 'pythonic' kullanımınızı belki WWGD olarak (FP Python için küçümsemesi iyi bilinir) ve mutlaka PEP8 / 20'yi tatmin etmek zorunda değil.
cowbert

1
el = x[4] if len(x) == 4 else 'No'- yani len(x) > 4? x[4], sınırların dışındaysa len(x) == 4.
mic

4

Sözlükler aramalar içindir. Bir girişin olup olmadığını sormak mantıklıdır. Listeler genellikle yinelenir. L [10] 'un var olup olmadığını sormak yaygın değildir, aksine L uzunluğunun 11 olup olmadığını sormak yaygındır.


Evet, sana katılıyorum. Ancak "/ group / Page_name" sayfasının göreli URL'sini ayrıştırdım. '/' İle bölün ve SayfaAdının belirli bir sayfaya eşit olup olmadığını kontrol etmek istedim. Uzunluk için ekstra kontrol yapmak veya istisna yakalamak veya kendi işlevini yazmak yerine [url.split ('/'). Get_from_index (2, None) == "lalala"] gibi bir şey yazmak rahat olur. Muhtemelen haklısın, sıra dışı olduğu düşünülüyor. Her neyse hala buna katılmıyorum =)
CSZ

@Nick Bastin: Yanlış bir şey yok. Her şey kodlamanın basitliği ve hızı ile ilgilidir.
CSZ

Anahtarların ardışık ints olduğu durumlarda listeleri daha fazla yer tasarrufu sağlayan bir sözlük olarak kullanmak istiyorsanız da yararlı olacaktır. Tabii ki negatif indekslemenin varlığı bunu zaten durduruyor.
Antimon

-1

Usecase'iniz temel olarak sadece sabit uzunlukta diziler ve matrisler yaparken önemlidir, böylece ne kadar süre önce elinizde olduklarını bilirsiniz. Bu durumda, bunları genellikle Yok veya 0 ile doldurmadan önce de yaratırsınız, böylece kullanacağınız herhangi bir dizin zaten var olur.

Bunu söyleyebilirsin: Sözlüklerde sık sık .get () lazım. Tam zamanlı bir programcı olarak on yıl sonra, bir listeye hiç ihtiyaç duyduğumu düşünmüyorum. :)


Yorumlardaki örneğime ne dersiniz? Daha basit ve okunabilir olan nedir? (url.split ('/'). getFromIndex (2) == "lalala") VEYA (sonuç = url.split (); len (sonuç)> 2 ve sonuç [2] == "lalala"). Ve evet, böyle bir işlevi kendim yazabileceğimi biliyorum =) ama böyle bir işlevin yerleşik olmadığı için şaşırdım.
CSZ

1
Senin durumunda yanlış yaptığını söyleyeceğim. URL işleme, yollar (kalıp eşleme) veya nesne geçişi ile yapılmalıdır. Ama, bu sorununuzu cevap: 'lalala' in url.split('/')[2:]. Ancak buradaki çözümünüzle ilgili sorun, sadece ikinci öğeye bakmanızdır. URL '/ monkeybonkey / lalala' ise ne olur? TrueURL geçersiz olsa bile bir alacaksınız .
Lennart Regebro

Sadece ikinci element aldım çünkü sadece ikinci elemente ihtiyacım vardı. Ama evet, dilimler iyi bir çalışma alternatifi gibi görünüyor
CSZ

@CSZ: Ama sonra ilk eleman yok sayılır ve bu durumda atlayabilirsiniz. :) Ne demek istediğimi görün, örnek gerçek hayatta o kadar iyi çalışmıyor.
Lennart Regebro
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.