Python'da list.index'i (mevcut olmayabilir) işlemenin en iyi yolu?


113

Şuna benzer bir kodum var:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

tamam yani bu basitleştirildi ama anladınız. Şimdi thingaslında listede olmayabilir, bu durumda -1'i olarak geçirmek istiyorum thing_index. Diğer dillerde index(), öğeyi bulamazsa döndürmeyi umduğunuz şey budur . Aslında bir ValueError.

Bunu yapabilirim:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

Ama bu kirli hissettiriyor, ayrıca ValueErrorbaşka bir nedenden dolayı yükseltilebilir mi bilmiyorum . Jeneratör işlevlerine göre aşağıdaki çözümü buldum, ancak biraz karmaşık görünüyor:

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

Aynı şeyi elde etmenin daha temiz bir yolu var mı? Listenin sıralanmadığını varsayalım.


4
"... bu durumda -1'i olarak geçirmek istiyorum thing_index." - Bu kesinlikle Pythonic değil. Bir işlemin başarılı olmaması durumunda (anlamsız) bir belirteç değeri iletmek hoş karşılanmaz - burada istisnalar gerçekten doğru yoldur. Özellikle thing_list[-1]geçerli bir ifade olduğundan, listedeki son girdi anlamına gelir.
Tim Pietzcker

@jellybean: facepalm ... java
coder'ı bulun

4
@Tim: str.findTam olarak bunu yapan bir yöntem var: öznede -1iğne bulunmadığında geri döner .
SilentGhost

@Tim Hiçbiri o zaman daha iyi olmazdı ... ve bu dict [key] ile dict.get [key] arasında benzer olacaktır
Draemon

@SilentGhost: Hm, ilginç. Buna daha detaylı bakmam gerekebilir. str.index()arama dizesi bulunmazsa bir istisna oluşturur.
Tim Pietzcker

Yanıtlar:


66

Try-exclude deyimini kullanmanın "kirli" bir tarafı yoktur. Bu pitonik yoldur. sadece yöntemle ValueErroryükseltilecektir .index, çünkü orada sahip olduğunuz tek kod budur!

Yoruma cevap vermek gerekirse:
Python'da, affetmeyi istemek izin almaktan daha kolay felsefe sağlam bir şekilde oluşturulmuştur ve hiç kimse index bu tür bir hatayı başka sorunlar için ortaya çıkarmayacaktır. Aklıma geldiğinden değil.


29
Elbette istisnalar istisnai durumlar içindir ve bu pek de öyle değildir. İstisna ValueError'dan daha spesifik olsaydı böyle bir sorun yaşamazdım.
Draemon

1
Sadece bu yöntemle atılabileceğini biliyorum, ancak yalnızca bu nedenle atılması garantili mi? İndeksin başarısız olmasının başka bir nedeni olduğunu düşünemiyorum ... ama o zaman tam olarak aklınıza gelmeyen şeyler için istisnalar değil mi?
Draemon

4
{}.get(index, '')Daha pitonik değil mi? Daha kısa, daha okunaklı bahsetmiyorum bile.
Esteban Küber

1
Ben emin değilim ne zaman anahtar vardır ve dict.get (anahtar) bekliyoruz zaman dicti [anahtar] kullanıyorum ve am burada bir şey eşdeğer arıyorum. None-1 yerine dönmek iyi olur, ancak kendiniz yorumladığınız gibi str.find () -1 döndürür, öyleyse neden aynı şeyi yapan list.find () olmasın? "
Pythonic

3
Ancak asıl mesele şu ki, en pitonik çözüm, -1 sentinel değerini değil, sadece try / exclude kullanmaktır . IE yeniden yazmalısın otherfunction. Öte yandan, kırılmadıysa, ...
Andrew Jaffe

53
thing_index = thing_list.index(elem) if elem in thing_list else -1

Tek çizgi. Basit. İstisna yok.


35
Basit evet, ancak bu iki doğrusal arama yapacak ve performans kendiliğinden bir sorun olmasa da aşırı görünüyor.
Draemon

4
@Draemon: Katılıyorum - bu 2 geçiş yapacak - ancak bunun bin satırlık bir kod tabanından darboğaz olması pek olası değil. :) ile zorunlu bir çözüme her zaman katılabilirsiniz for.
Emil Ivanov

lambd ileindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa Akiel

17

dictTipi bir sahip getişlevi anahtar sözlükte yoksa, 2nd argüman getbunun dönmesi gerektiğini değerdir. Benzer şekilde , anahtar varsa setdefaultiçindeki değeri döndüren dict, aksi takdirde değeri varsayılan parametrenize göre ayarlar ve ardından varsayılan parametrenizi döndürür.

listBir getindexdefaultyönteme sahip olmak için türü genişletebilirsiniz .

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

Hangisi daha sonra şu şekilde kullanılabilir:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

6

Kullanılan kodunuzda yanlış bir şey yok ValueError. İstisnalardan kaçınmak istiyorsanız, işte bir başka satır:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

Bu python 2.6 mı? Bundan bahsetmediğimi biliyorum ama 2.5 kullanıyorum. Muhtemelen 2.6'da yapacağım şey
buydu

1
@Draemon: Evet, next()işlev Python 2.6+ sürümünde mevcuttur. Ancak 2.5 için uygulanması kolaydır, Python 2.5 için next () işlev uygulamasına bakın
jfs

4

Bu mesele dil felsefesinden biridir. Örneğin Java'da, istisnaların gerçekten sadece "istisnai durumlarda", yani hataların meydana geldiği zaman, akış kontrolü için kullanılması gerektiği geleneği vardır . Başlangıçta bu, Java istisnaları yavaş olduğu için performans nedenlerinden kaynaklanıyordu, ancak şimdi bu kabul edilen bir stil haline geldi.

Buna karşılık Python, ValueErrorburada tartıştığımız gibi a yükseltmesi gibi normal program akışını belirtmek için her zaman istisnalar kullanmıştır . Python tarzında bunda "kirli" bir şey yok ve bunun geldiği yerde çok daha fazlası var. Daha da yaygın bir örnek, bir yineleyicinin yöntemi tarafından başka değerlerin olmadığını belirtmek için ortaya çıkan StopIterationistisnadırnext() .


Aslında JDK atar yolu ben emin felsefesi aslında Java uygulanmasını değilim bu yüzden, çok fazla kontrol istisnalar. Tek başına bir sorunum yok StopIterationçünkü istisnanın ne anlama geldiği açıkça tanımlanmış. ValueErrorsadece biraz fazla genel.
Draemon

Akış kontrolü için istisnaların kullanılmaması gerektiği fikrine atıfta bulundum : c2.com/cgi/wiki? / CheckedExceptions
Tendayi Mawushe

4

Bunu sık sık yapıyorsanız, yardımcı bir işlevde pişirmek daha iyidir:

def index_of(val, in_list):
    try:
        return in_list.index(val)
    except ValueError:
        return -1 

4

Peki ya bu 😃:

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 

1

Peki buna ne dersin:

otherfunction(thing_collection, thing)

Bir işlev arabirimindeki bir liste dizini gibi uygulamaya bağımlı bir şeyi açığa çıkarmak yerine, koleksiyonu ve şeyi iletin ve diğer işlevin "üyelik testi" sorunlarıyla ilgilenmesine izin verin. Diğer işlev koleksiyon türü agnostik olarak yazılırsa, muhtemelen şununla başlar:

if thing in thing_collection:
    ... proceed with operation on thing

thing_collection bir liste, tuple, küme veya dikteyse işe yarayacaktır.

Bu muhtemelen şunlardan daha açıktır:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

Bu, başka bir işlevde zaten sahip olduğunuz koddur.


1

Peki ya bunun gibi:

temp_inx = (L + [x]).index(x) 
inx = temp_inx if temp_inx < len(L) else -1

0

Listelerde ".index ()" yöntemiyle aynı sorunu yaşıyorum. Bunun bir istisna oluşturması gerçeğiyle ilgili bir sorunum yok ama bunun tanımlayıcı olmayan bir Değer Hatası olduğu gerçeğine kesinlikle katılmıyorum. Yine de bir IndexError olsaydı anlayabilirdim.

Python'da geçerli bir indeks olduğu için "-1" döndürmenin neden bir sorun olduğunu anlayabiliyorum. Ama gerçekçi, ben asla bir ".Index ()" metodu negatif bir sayı döndürür bekliyoruz.

İşte tek satırlık bir satır (tamam, bu oldukça uzun bir satır ...), listeden tam olarak bir kez geçiyor ve öğe bulunamazsa "Yok" u döndürüyor. Eğer çok arzu ederseniz, -1 değerini döndürmek için yeniden yazmak önemsiz olurdu.

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

Nasıl kullanılır:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>

-2

Neden kirli olduğunu düşünmen gerektiğini bilmiyorum ... istisna yüzünden? bir oneliner istiyorsanız, işte burada:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

ama kullanmamanızı tavsiye ederim; Ross Rogers çözümünün en iyisi olduğunu düşünüyorum, arzulanan davranışınızı özetlemek için bir nesne kullanın, okunabilirlik pahasına dili sınırlarına zorlamayı denemeyin.


1
Evet, istisna yüzünden. Kodunuz iki doğrusal arama yapacak değil mi? Burada performans gerçekten önemli değil. SuperDuperList çözümü güzel, ancak bu özel durumda aşırıya kaçmış gibi görünüyor. Sanırım sadece istisnayı yakalayacağım, ama daha temiz (estetiğim için) bir yol olup olmadığını görmek istedim.
Draemon

@Draemon: Elinizdeki kodu find()işleve dahil edeceksiniz ve hepsi temiz olacak;)
SilentGhost

1
Cevabımın iki olumsuz oyu olması ilginçtir; Emil Ivanov'unki ise semantik olarak aynı olsa da en çok oy alanlardan biridir. Muhtemelen bu, benimki daha yavaş olduğu için oluyor, çünkü "in" operatörü yerine count () kullanıyorum ... en azından bunun harika olacağını söyleyen bir yorum :-)
Alan Franzoni

-2

Ben şunu öneririm:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)

2
Bu çözümle ilgili sorun, "-1" 'in geçerli bir liste dizini olmasıdır (son dizin; sondan ilk). Bunu halletmenin daha iyi bir yolu, durumunuzun ilk dalında Yanlış döndürmek olacaktır.
FanaticD
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.