Python döngülerinin "else" maddesini nasıl anlayabilirim?


191

Birçok Python programcısı muhtemelen whiledöngülerin ve fordöngülerin sözdiziminin isteğe bağlı bir else:cümle içerdiğinin farkında değildir :

for val in iterable:
    do_something(val)
else:
    clean_up()

Vücut elsemaddesi, temizlik hareketleri belirli türdeki iyi bir yerdir ve döngü normal fesih da yürütülüyor: ile döngüsünden çıkar, Ie returnveya breakatlar elsemaddesini; bir continueyürütmeden sonra çıkılıyor . Ben sadece çünkü sadece bu biliyorum baktım ben hatırlayamıyorum, çünkü (yine) ne zamanelse bloğu icra edilir.

Her zaman? Adından da anlaşılacağı gibi, döngü "hatası"? Düzenli fesih mi? Döngüden çıkılsa bile return? Bakmadan asla tam olarak emin olamam.

Anahtar kelime seçimindeki belirsizliğimi suçluyorum: elseBu anlambilim için inanılmaz derecede anımsatıcı değilim. Benim sorum "bu anahtar kelime neden bu amaç için kullanılıyor" değil (muhtemelen cevapları ve yorumları okuduktan sonra kapatmak için oy vereceğim) değil, ama anlambiliminin anlamlı olması için elseanahtar kelimeyi nasıl düşünebilirim ve ben bu yüzden hatırlıyor musun?

Bu konuda oldukça fazla tartışma olduğundan eminim ve seçimin tryifadenin tutarlılığına else:(ayrıca da bakmam gerekiyor) ve Python'un ayrılmış sözleri. Belki de seçim nedenleri elseişlevini açıklığa kavuşturacak ve daha akılda kalıcı hale getirecektir, ancak ismin işleve bağlandıktan sonra değil, tarihsel açıklamadan sonra değil.

Sorumun bir nüshası olarak kısaca kapatıldığı bu sorunun cevapları , birçok ilginç arka hikaye içeriyor. Sorumun farklı bir odağı var ( elseanahtar kelime seçimiyle belirli anlambilimi nasıl bağlayacağım ), ancak bu soruya bir yerde bir bağlantı olması gerektiğini hissediyorum.



4
Sanırım bu soruyu yazdıktan sonra hatırlayabilirsin :)
Jasper

11
elsearaçlar temelde "devamlılık koşulu başarısız olursa". Geleneksel bir döngü için, devam koşulu genellikle i < 42, bu durumda, bu bölümü şu şekilde görüntüleyebilirsinizif i < 42; execute the loop body; else; do that other thing
njzk2

1
Tüm bunlar doğrudur ve özellikle drawoc'un cevabını seviyorum, ancak dikkate alınması gereken başka bir şey , sözdizimi açısından da biraz mantıklı olan mevcut bir anahtar kelime olmasıdır. Sonunda denemek / hariç tutmak ve belki de denemek / hariç / sonunda, ama başka var - bir istisna olmadıysa bu kodu çalıştırın . Btw olanı, bu kodu try cümlesi altında itmekle aynı şey değildir - istisna işleme en iyi şekilde dar hedeflendiğinde kullanılır. Yani, kavramsal mantıklı olsa da - buradaki cevapların birçoğuna göre - bence anahtar kelimeyi oyunda tekrar kullanmak - bunu belirli koşullar altında çalıştırın .
JL Peyret

1
@Falanwe, koddan çıkıldığında bir fark var break. Standart kullanım durumu, döngü bir şey ararken ve onu bulduğunda kırılır. elseHiçbir şey bulunursa sadece yürütülür.
alexis

Yanıtlar:


212

(Bu ilham @ Mark Tolonen'in cevabıdır.)

Bir ififade else, koşulu yanlış olarak değerlendirilirse yan tümcesini çalıştırır . Aynı şekilde, bir whiledurum, durumu false olarak değerlendirilirse else yan tümcesini çalıştırır.

Bu kural tanımladığınız davranışla eşleşir:

  • Normal yürütmede, while döngüsü koşul false olarak değerlendirilinceye kadar art arda çalışır ve bu nedenle döngüden doğal olarak çıkıldığında başka deyimi çalıştırır.
  • Bir breakdeyimi yürüttüğünüzde , koşulu değerlendirmeden döngüden çıkarsınız, böylece koşul false olarak değerlendirilemez ve asla başka bir cümle çalıştırmazsınız.
  • Bir continuedeyimi yürüttüğünüzde , koşulu yeniden değerlendirirsiniz ve bir döngü yinelemesinin başlangıcında tam olarak ne yaparsanız yapın. Bu nedenle, koşul doğruysa, döngü yapmaya devam edersiniz, ancak yanlışsa başka bir cümle çalıştırırsınız.
  • Döngüden çıkmanın diğer yöntemleri, örneğin returndurumu değerlendirmez ve bu nedenle else yan tümcesini çalıştırmaz.

fordöngüler aynı şekilde davranır. Yineleyicinin daha fazla öğesi varsa durumu doğru, aksi halde yanlış olarak düşünün.


8
Bu çok mükemmel bir cevap. Döngülerinize bir dizi elif ifadesi gibi davranın ve diğer davranış doğal mantığını ortaya çıkaracaktır.
Nomenator

1
Ben de bu cevabı seviyorum ama bir dizi elififadeyle bir benzetme yapmıyor . Orada bir cevap yapar ve onu tek net upvote sahiptir.
alexis

2
tam olarak değil, bir while döngüsü, durumun hemen öncesinde False ile karşılaşmasını sağlayabilirdi, bu durumda koşmazdı break, elseancak koşul False. forDöngülere benzer şekilde breakson elemanda da olabilir.
Tadhg McDonald-Jensen

36

Daha Bunu şu şekilde düşünmek: elseblok olacak hep herşey giderse yürütülecek sağ önceki içinde forbu yorgunluk ulaşacağı şekilde bloğun.

Sağ bu bağlamda hiçbir anlamına gelecektir exceptionhayır, breakhayır, return. Kontrolü ele forgeçiren herhangi bir ifade , elsebloğun atlanmasına neden olur .


Bir öğede iterablearama yaparken, öğe bulunduğunda aramanın kapatıldığı veya "not found"aşağıdaki elseblok yoluyla bir bayrak kaldırıldığı / yazdırıldığı yaygın bir kullanım durumu bulunur :

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  

A continuekontrolünü ele geçirmez for, bu yüzden kontrol elsebittikten sonra devam eder for.


20
Sesler çok güzel ... ama o zaman bir beklediğiniz elsefıkra şeyler çalıştırılacak yok sen doğru gitmez ki? Zaten tekrar kafam karışıyor ...
alexis

Ben sizinle aynı fikirde "Teknik olarak [her anlamsal benzerlik değil else, çünkü]" elseBen de örnekleri görüldüğü gibi döngü şartların hiçbiri, True olarak değerlendirirken çalıştırılır Cevabıma
Tadhg McDonald- Jensen

@ TadhgMcDonald-Jensen Ayrıca a False. Yani nasıl soru forolan kırık kullanım durumuna göre değişir.
Musa Koledoye

Bu doğru, bir şekilde İngilizce'nin "else" (aslında python'un diğer kullanımlarına yansıyan) anlamıyla ne olduğunu ilişkilendirmenin bir yolunu istiyorum else. else@Moses ne yapar, ancak bu davranışı "else" ile nasıl ilişkilendirebileceğimize dair iyi sezgisel bir özet sağlarsınız. Farklı bir anahtar kelime kullanıldıysa (örneğin, ilgili bir sorunun bu cevabındanobreak belirtildiği gibi ), anlaşılması daha kolay olacaktır.
alexis

1
Gerçekten "işler doğru gidiyor" ile ilgisi yoktur. if/ whileKoşulu yanlış olarak değerlendirildiğinde veya foröğeler dışında olduğunda tamamen yürütülür . breakiçeren döngü var (sonra else). continuegeri döner ve döngü durumunu tekrar değerlendirir.
Mark Tolonen

31

Ne zaman bir ifyürütmeelse ? Durumu yanlış olduğunda. while/ İçin tamamen aynıdır else. Aklınıza gelebilecek Yani while/ ' elsesadece olarak ifyanlış değerlendirir kadar gerçek durumunu çalışmaya devam söyledi. A breakbunu değiştirmez. Değerlendirme içermeyen sadece döngü içerisinden atlar. elseKeşke yürütülür değerlendirirkenif / whiledurumunu yanlıştır.

forOnun sahte koşulu onun yineleyici yorucudur dışında benzerdir.

continueve breakyürütme else. Bu onların işlevi değil. breakİçeren döngü çıkar. continueDöngü koşulu değerlendirilir içeren döngünün, üstüne gider. Yürütme if/ whileyanlış yapma (veya forbaşka bir öğeye sahip olmayan) değerlendirme elseve başka bir yol yoktur.


1
Söyledikleriniz kulağa çok mantıklı geliyor, ancak "sonlandırma koşulu yanlış olana veya kırılana / devam edene kadar" üç sonlandırma koşulunu birlikte toparlamak yanlış: Ciddi olarak, elsedöngüden çıkıldığında continue(veya normalde) yan tümce yürütülür , ancak ile çıkarsak olmazbreak . Bu incelikler, neyi elseyakaladığını ve neyi yapmadığını gerçekten araştırmaya çalışıyorum .
alexis

4
@alexis evet Orada açıklığa kavuşmam gerekiyordu. Düzenlenen. continue, başka bir işlemi yürütmez, ancak daha sonra false olarak değerlendirilebilecek döngünün en üstüne geri döner.
Mark Tolonen

24

Aslında bunun anlamı şudur:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...

Bu ortak kalıbı yazmanın daha güzel bir yolu:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...

elseYantümce varsa yürütülmez return, çünkü returno amaçlandığı anlaşılacaktır, yaprakların fonksiyonu. Aklınıza gelebilecek tek istisna finally, amacı her zaman yürütüldüğünden emin olmaktır.

continuebu konuyla özel bir ilgisi yok. Döngünün geçerli yinelemesinin sona ermesine neden olur, bu da tüm döngüyü bitirebilir ve bu durumda döngü a ile sona ermemiştir break.

try/else benzer:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...

20

Döngülerinizi buna benzer bir yapı olarak düşünüyorsanız (biraz sahte kod):

loop:
if condition then

   ... //execute body
   goto loop
else
   ...

biraz daha mantıklı olabilir. Bir döngü aslında sadece ifkoşul olana kadar yinelenen bir ifadedir false. Ve önemli olan nokta bu. Döngü durumunu kontrol eder vefalse , böylece else(normal gibiif/else ) ve sonra döngü yapılır.

Bu nedenle else , koşul kontrol edildiğinde yalnızca get'in yürütüldüğüne dikkat edin . Bu, örneğin a returnveya a ile yürütmenin ortasında döngü gövdesinden çıkarsanız,break , koşul tekrar kontrol edilmediğinden,else durum yürütülmez.

continueÖte yandan A , geçerli yürütmeyi durdurur ve daha sonra döngünün durumunu tekrar kontrol etmek için geri atlar, bu yüzden elsebu senaryoda ulaşılabilir.


Bu cevabı çok beğendim, ancak basitleştirebilirsiniz: endEtiketi atlayın ve sadece vücudun goto loopiçine yerleştirin if. Belki ifde etiketle aynı çizgiyi koyarak outdent bile ve aniden orignal gibi görünüyor.
Bergi

@Bergi Evet, sanırım bu biraz daha açık, teşekkürler.
Keiwan

15

Döngünün elsemaddesiyle ilgili anım , Raymond Hettinger tarafından nasıl çağrılması gerektiğini düşündüğünü anlatan bir konuşmayı izlediğim zamandı nobreak. Aşağıdaki koda bir bakın, ne yapacağını düşünüyorsunuz?

for i in range(10):
    if test(i):
        break
    # ... work with i
nobreak:
    print('Loop completed')

Ne olacağını tahmin ederdin? Peki, bu kısım nobreaksadece breakdöngüde bir cümle vurulmamışsa yürütülür .


8

Genellikle böyle bir döngü yapısını düşünme eğilimindeyim:

for item in my_sequence:
    if logic(item):
        do_something(item)
        break

Değişken sayıda if/elififadeye çok benzemek için :

if logic(my_seq[0]):
    do_something(my_seq[0])
elif logic(my_seq[1]):
    do_something(my_seq[1])
elif logic(my_seq[2]):
    do_something(my_seq[2])
....
elif logic(my_seq[-1]):
    do_something(my_seq[-1])

Bu durumda elsefor döngüsündeki elsedeyim, elifs zincirindeki deyim gibi çalışır , ancak koşullardan hiçbiri True olarak değerlendirilmeden önce yürütülür. (veya yürütme ile returnbir istisna veya bir istisna) Döngüm bu spesifikasyona uymuyorsa, genellikle for: elsebu soruyu yayınladığınız nedenden dolayı kullanmayı tercih etmeyi seçerim : sezgisel değildir.


Sağ. Ancak bir döngü birden çok kez çalışır, bu yüzden bunu for-loop'a nasıl uygulayacağınız biraz belirsizdir. Açıklayabilir misin?
alexis

@alexis Cevabımı yeniden yaptım, şimdi çok daha net olduğunu düşünüyorum.
Tadhg McDonald-Jensen

7

Diğerleri zaten mekaniği açıkladık while/for...elseve Python 3 dil referans (bkz yetkili tanıma sahip iken ve için ), ama burada benim kişisel anımsatıcı FWIW olduğunu. Sanırım benim için anahtar bunu iki parçaya ayırmak oldu: biri elsedöngü koşuluyla ilişkili anlamını anlamak için ve diğeri döngü kontrolünü anlamak için.

Anlayarak başlamak en kolay yolu buluyorum while...else:

whileDaha fazla eşyaya sahipsin, bir şeyler yap, elsebiterse, bunu yap

for...elseAnımsatıcı temelde aynıdır:

forher öğe, bir şeyler yapın, ama elsebiterse, bunu yapın

Her iki durumda da, elseparçaya yalnızca işlenecek başka öğe kalmadığında ve son öğe düzenli bir şekilde işlendikten sonra ulaşılır (örn.break veya return). A continuesadece geri döner ve daha fazla öğe olup olmadığını görür. Bu kuralların için My anımsatıcı için geçerlidir whileve for:

ne zaman breaking veya returning , elseyapacak bir şey yok
ve dediğimde continue, bu sizin için "geri dönmek için geri dön"

- "başlamak için geri dön" ile, açıkçası, yinelenebilirde daha fazla öğe olup olmadığını kontrol ettiğimiz döngü başlangıcı else , söz konusu olduğunda, continuegerçekten hiçbir rol oynamaz.


4
Bir for / else döngüsünün genel amacının aradığınızı bulana ve durdurmak istediğinize veya öğeleri tükenene kadar öğeleri incelemek olduğunu söyleyerek geliştirilebileceğini öneririm . "Else", "(aradığınızı bulamadan) eşyalarınızın bitmesi" bölümünü işlemek için vardır.
supercat

@supercat: Olabilir, ama orada en yaygın kullanımların ne olduğunu bilmiyorum. elseAyrıca sadece tüm öğeleri bittiğinde bir şey yapmak için kullanılabilir. Örnek olarak bir günlük girişi yazmak, bir kullanıcı arabirimini güncellemek veya yaptığınız diğer işlemleri bildirmek verilebilir. Hiçbir şey gerçekten. Ayrıca, bazı kod parçaları breakdöngü içinde "başarılı" vaka sonuna sahiptir elseve yineleme sırasında uygun bir öğe bulamadığınız "hata" durumunu işlemek için kullanılır (belki de düşündüğünüz buydu) nın-nin?).
Fabian Fagerholm

1
Düşündüğüm dava tam da başarılı davanın bir "mola" ile bittiği ve "başka" nın başarı eksikliğini ele aldığı durumdu. Bir döngü içinde "break" yoksa, "else" kodu da çevrimi bloğun bir parçası olarak döngüyü takip edebilir.
supercat

Döngünün tüm yinelenebilir öğelerden kesintisiz olarak geçtiği durum (ve bu başarılı bir durumdu) ve bunun olmadığı durum arasında ayrım yapmanız gerekmediği sürece. Daha sonra "sonlandırma" kodunu döngü elsebloğuna koymanız veya başka araçlar kullanarak sonucu takip etmeniz gerekir . Temel olarak katılıyorum, sadece insanların bu özelliği nasıl kullandıklarını bilmediğimi söylüyorum ve bu nedenle " elsebaşarılı davayı ele alıyor" senaryosu veya " elsebaşarısız davayı ele alıyor" senaryosunun daha yaygın olup olmadığına dair varsayımlarda bulunmaktan kaçınmak istiyorum . Ama iyi bir noktaya sahipsin, bu yüzden yorum iptal edildi!
Fabian Fagerholm

7

In gelişme Test güdümlü kullanırken, (TDD) Dönüşüm Öncelik öncül paradigma, koşullu ifadelerin bir genelleme olarak döngüler davranın.

Yalnızca basit if/else(hayır elif) ifadeleri göz önünde bulundurursanız, bu yaklaşım bu sözdizimi ile iyi bir şekilde birleşir :

if cond:
    # 1
else:
    # 2

genelleme:

while cond:  # <-- generalization
    # 1
else:
    # 2

güzelce.

Diğer bir deyişle, TDD tek bir vakadan koleksiyonları olan vakalara doğru adımlar daha fazla yeniden düzenleme gerektirir.


İşte 8thlight blogundan bir örnek :

8thlight blogundaki bağlantılı makalede, Word Wrap kata dikkate alınır: sbelirli bir genişliğe (aşağıdaki snippet'lerde değişken) sığdırmak için dizelere satır kesmeleri ( aşağıdaki snippet'lerde değişken) ekleme length. Bir noktada uygulama aşağıdaki gibi görünür (Java):

String result = "";
if (s.length() > length) {
    result = s.substring(0, length) + "\n" + s.substring(length);
} else {
    result = s;
}
return result;

ve şu anda başarısız olan bir sonraki test:

@Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
    assertThat(wrap("verylongword", 4), is("very\nlong\nword"));
    }

Koşullu olarak çalışan bir kodumuz var: belirli bir koşul karşılandığında satır sonu eklenir. Birden çok satır sonunu işlemek için kodu geliştirmek istiyoruz. Makalede sunulan çözüm, (if-> while) dönüşümünü , ancak yazar şu yorumu yapar:

Döngüler elseyan tümcelere sahip olamazken,else yolu daha az yaparak yoluif . Yine, bu bir yeniden düzenleme.

başarısız bir test bağlamında kodda daha fazla değişiklik yapmaya zorlar:

String result = "";
while (s.length() > length) {
    result += s.substring(0, length) + "\n";
    s = s.substring(length);
}
result += s;

TDD'de testleri geçmek için mümkün olduğunca az kod yazmak istiyoruz. Python'un sözdizimi sayesinde aşağıdaki dönüşüm mümkündür:

dan:

result = ""
if len(s) > length:
    result = s[0:length] + "\n"
    s = s[length:]
else:
    result += s

için:

result = ""
while len(s) > length:
    result += s[0:length] + "\n"
    s = s[length:]
else:
    result += s

6

Gördüğüm şekilde else:, döngünün sonunu geçtiğinizde ateşlenir.

Siz breakya returnda ya da raisedöngü sonunu aşmazsanız, hemen durursunuz ve böylece else:blok çalışmaz. Eğer varsa continuedöngünün sonuna geçmiş hala yinelerler devam beri hemen yanında yineleme atlar. Döngüyü durdurmaz.


1
Bunu sevdim, sanırım bir şeye devam ediyorsun. Döngü anahtar kelimelerinden önceki kötü eski günlerde döngülerin nasıl uygulandığına biraz bağlı. (Yani: kontrol, döngünün alt kısmına yerleştirildi, gotobaşarı üstte kaldı.) Ama en çok oy alan cevabın daha kısa bir versiyonu ...
alexis

@alexis, subjektif, ama düşünmeyi daha kolay ifade etme yolumu buluyorum.
Winston Ewert

aslında katılıyorum. Sadece daha zeki olduğu için.
alexis

4

Maddeyi elsedöngü yapısının bir parçası olarak düşünün ; breakdöngü yapısından tamamen kopar ve böyleceelse yan .

Ama gerçekten, zihinsel eşlemem basitçe desen C / C ++ deseninin 'yapılandırılmış' versiyonu:

  for (...) {
    ...
    if (test) { goto done; }
    ...
  }
  ...
done:
  ...

Yani, for...elsekendimle karşılaştığımda ya da yazdığımda, doğrudan anlamak yerine , onu zihinsel olarak yukarıdaki örüntü anlayışına çeviriyorum ve sonra python sözdiziminin hangi bölümlerinin örüntünün hangi bölümlerine eşlendiğini anlıyorum.

(Korku tırnak içine 'yapılandırılmış' koydum çünkü fark kodun yapılandırılmış veya yapılandırılmamış olması değil, sadece belirli yapıya adanmış anahtar kelimeler ve dilbilgisi olup olmadığıdır)


1
Nerede else? Eğer demekse done:vekil durmak etiket veya else:ben tam olarak geriye olduğuna inanıyorum.
alexis

@alexis 'else' kodu , etiketten hemen önce '...' ifadesini doldurur done:. Genel yazışma, belki de en iyi şekilde şöyle söylenir: Python, else-on-döngü yapısına sahiptir, böylece bu kontrol akış modelini olmadan ifade edebilirsiniz goto.
zwol

Bu kontrol akış düzenini yürütmenin başka yolları da vardır, örneğin bir bayrak ayarlayarak. Kaçınılması gereken de budur else.
alexis

2

Eğer eşleştirirseniz elseile for, bu kafa karıştırıcı olabilir. Anahtar kelimenin elsebu sözdizimi için mükemmel bir seçim olduğunu düşünmüyorum , ancak hangisini içeren elseile ifeşlerseniz break, bunun gerçekten mantıklı olduğunu görebilirsiniz. elseönceki bir ififade yoksa zar zor yararlıdır ve ben bu yüzden sözdizimi tasarımcı anahtar kelimeyi seçti inanıyorum.

İnsan dilinde göstereyim.

forşüpheli bir gruptaki her kişi soruşturmanın ifsuçlu olduğunu break. elserapor hatası.


1

Bunu düşünmek yolu, anahtar anlamını ele almaktır continueziyade else.

Bahsettiğiniz diğer anahtar kelimeler döngüden çıkar (anormal olarak çıkar) continue , ancak döngü içinde kod bloğunun geri kalanını atlıyor. Döngü sonlandırmadan önce gelmesi rastlantısaldır: sonlandırma, döngü koşullu ifadesinin değerlendirilmesi ile gerçekte normal şekilde yapılır.

O zaman elseyan tümcenin normal döngü sonlandırmasından sonra yürütüldüğünü hatırlamanız gerekir .


0
# tested in Python 3.6.4
def buy_fruit(fruits):
    '''I translate the 'else' below into 'if no break' from for loop '''
    for fruit in fruits:
        if 'rotten' in fruit:
            print(f'do not want to buy {fruit}')
            break
    else:  #if no break
        print(f'ready to buy {fruits}')


if __name__ == '__main__':
    a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
    b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
    buy_fruit(a_bag_of_apples)
    buy_fruit(b_bag_of_apples)

'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
'''
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.