İstisnadan sonra nasıl tekrar denenir?


252

İle başlayan bir döngü var for i in range(0, 100). Normalde düzgün çalışır, ancak bazen ağ koşulları nedeniyle başarısız olur. Şu anda ben hata durumunda, continuehariç yan tümce (sonraki numara için devam) olacak şekilde ayarladım i.

iDöngünün başarısız yinelemesine aynı numarayı yeniden atayabilir ve tekrar çalışabilir miyim?


1
range(100)İlk parametre olmadan kullanabilirsiniz . Kullanabileceğiniz Python 2.x kullanıyorsanız xrange(100), bu bir yineleyici oluşturur ve daha az bellek kullanır. (Sadece 100 nesne ile önemli değil.)
Georg Schölly


2
o iplikte arbiter istisnaları ele alma desteği ile dekoratörler kullanarak çok zarif bir çözüm var
zitroneneis

Yanıtlar:


380

while TrueFor döngüsü içinde bir iç yapın, trykodunuzu içine koyun ve whileyalnızca kodunuz başarılı olduğunda bu döngüden kopun.

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

31
@Ignacio, ha ? continueyeniden deneme whiledöngü, elbette, değilfor , bu yüzden (!) iolduğu değil "sonraki" şey - bu aynı bir önceki (başarısız) bacak olduğu gibi aynısı whileelbette.
Alex Martelli

13
Xorsyst'in belirttiği gibi, buraya bir yeniden deneme sınırı koymanız önerilir. Aksi takdirde oldukça uzun bir süre döngüde kalabilirsin.
Brad Koch

2
Bu mükemmel bir örnek: medium.com/@echohack/…
Tony Melony

7
Kesinlikle süre True: satır dışında bırakacaktı, aksi takdirde mola tükenme dış döngü devam edecektir.
Ocak

1
@Sankalp, bana öyle geliyor ki bu cevap soru metni için uygun.
Ocak'ta zneak

189

Yeniden deneme sayısını sınırlamayı tercih ederim, böylece belirli bir öğeyle ilgili bir sorun varsa sonunda bir sonrakine devam edersiniz, böylece:

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

3
@ g33kz0r Python'daki for-else yapısı, for döngüsü bozulmazsa else yan tümcesini çalıştırır. Bu durumda, bu bölüm 10 denemenin tümünü dener ve her zaman bir istisna alırsak yürütülür.
xorsyst

7
Bu harika bir cevap! Gerçekten çok daha fazla oyu hak ediyor. Python'daki tüm tesisleri, özellikle de daha az bilinen else:maddeyi mükemmel bir şekilde kullanır for.
pepoluan

2
Deneyin sonunda bir ara vermeye gerek yok mu? Deneyin ek molası ile, işlem başarıyla tamamlanırsa döngü kopar, başarıyla tamamlanmazsa doğrudan istisna kısmına gidecektir. bu mantıklı mı? Denemenin sonunda bir mola vermezsem: sadece 100 kez yapar.
Tristan

1
@Tristan - aradığın elsemadde try"başarılı, sonra kırmak" yapar.
PaulMcG

1
Ayrıca yeniden denemek için bir for-loop tercih ediyorum. Bu koddaki bir kırışıklık, denemeden vazgeçtiğinizde istisnayı yeniden yükseltmek istiyorsanız, exceptyan tümce içinde "teşebbüs = 9:
yükselt

69

Yeniden deneme paketi , hata durumunda bir kod bloğunu yeniden denemenin güzel bir yoludur.

Örneğin:

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

4
: Daha genel olarak pypi birden yeniden deneme dekoratörler için paketlere sahip pypi.python.org/...
Kert

yine de her başarısız olduğunda yeniden deneme girişiminin sayısını yazdırabilirsiniz?
dim_user

8
Anladığım gibi, daha aktif çatal github.com/jd/tenacity ve belki github.com/litl/backoff da kullanılabilir.
Alexey Çalı

23

İşte diğerlerine benzer bir çözüm, ancak öngörülen sayı veya yeniden denemede başarılı olmazsa istisnayı artıracaktır.

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

Güzel cevap, ancak değişken adı retriesyanıltıcı. Daha çok olmalı tries.
Lukas

True @ Lukas. Sabit.
TheHerk

Çok iyi bir çözüm teşekkür ederim. Her deneme arasında bir gecikme eklenerek geliştirilebilir. API'lerle uğraşırken çok kullanışlıdır.
Sam

14

Bu çirkin iken döngüler kullanmadan daha "fonksiyonel" yaklaşım:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

13
Üzgünüm, ama "çirkin iken döngüler" varyantlarından çok daha çirkin görünüyor; ve fonksiyonel programlamaya bayılıyorum ...
lvella

9
Yine de derinlemesine dinlemediğinizden emin olmanız gerekir - Python'daki varsayılan yığın boyutu 1000'dir
Cal Paterson

5
Bu 'işlevsel' olacaksa, özyineleme şöyle olmalıdır:except: tryAgain(retries+1)
quamrana

Buradaki sorun, hatayı değişkenler olarak geçirmemiz gerektiğidir.
lowzhao

11

En açık yol açık bir şekilde ayarlamak olacaktır i. Örneğin:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

37
Bu C veya C ++ mı? Söyleyemem.
Georg Schölly

5
@Georg Bu Python, soruda belirtildiği gibi. Ya da bir nedenden dolayı alaycı olduğunuz?
Jakob Borg

3
Bu OP'nin istediği şeyi yapmaz. i += 1Sonra koyarsanız olabilir # do stuff.
fmalina

5
Pitonik değil. rangeBu tür şeyler için kullanmalı .
Mystic

2
Kabul ediyorum, bu kesinlikle menzili kullanmalı.
user2662833

5

Zaman aşımı olan genel bir çözüm:

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

Kullanımı:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

Hata kontrolü için ayrı bir fonksiyon belirtmek mümkün müdür? Geri arama çıktısını alır ve basit kullanmak yerine bir başarısızlık veya başarı olup olmadığına karar vermek için hata kontrol işlevine except exception:
geçecektir

Bunun yerine try … exceptbir ififade kullanabilirsiniz . Ancak daha az pitoniktir.
Laurent LAPORTE

Bu çözüm işe yaramıyor. trinket.io/python/caeead4f6b do_stuff tarafından atılan istisna jeneratöre kabarcık oluşturmaz. Neden ki? do_stuff, jeneratörde iç içe olmayan, kendi başına bir dış seviyede olan for döngüsünün gövdesinde çağrılır.
isarandi

Hakkınız, ancak farklı bir nedenden dolayı: callbackişlev asla çağrılmaz. Parantezi unuttum, yerine koyun callback().
Laurent LAPORTE

5
for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

Sürümüm, yukarıdakilerden birkaçına benzer, ancak ayrı bir whiledöngü kullanmaz ve tüm yeniden denemeler başarısız olursa en son istisnayı yeniden yükseltir. Açık bir şekilde err = Noneüstte belirlenebilir, ancak yalnızca elsebir hata varsa ve bu nedenle errayarlanmışsa son bloğu yürütmesi gerektiği için kesinlikle gerekli değildir .



4

While ve counter kullanma:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

4

Özyineleme kullanma

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

1
Çıkış koşulu mu? Yoksa bu 100 * sonsuzluk mu çalıştırıyor?
ingyhere

3

Python yeniden deneme paketini kullanabilirsiniz. Tekrar deniyor

Hemen hemen her şeye yeniden deneme davranışı ekleme görevini basitleştirmek için Python'da yazılmıştır.


2

Alternatifleri retrying: tenacityve backoff(2020 güncellemesi)

Yeniden deneme kütüphanesi daha önce gitmenin yoluydu, ancak ne yazık ki bazı hataları var ve 2016'dan beri herhangi bir güncelleme yok. Diğer alternatifler geri çekilme ve azim gibi görünüyor . Bunu yazarken, azim daha fazla GItHub yıldızına (2.3k vs 1.2k) sahipti ve daha yakın zamanda güncellendi, bu yüzden kullanmayı seçtim. İşte bir örnek:

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

Yukarıdaki kod şöyle bir çıktı verir:

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

İçin daha fazla ayar tenacity.retry, dayanıklılık GitHub sayfasında listelenir .


1

Yuvalanmış döngülere sahip olmayan ve breakbaşarıyı çağıran bir çözüm istiyorsanız, geliştirilebilir retriableher şey için hızlı bir sargı geliştirebilirsiniz . Sık sık karşılaştığım bir ağ sorunu örneği - kaydedilmiş kimlik doğrulamasının süresi doluyor. Bunun kullanımı şöyle olacaktır:

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

1

Kodlarımda aşağıdakileri kullanıyorum,

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

0

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>


0

İşte bu konuda benim almak. Aşağıdaki retryişlev aşağıdaki özellikleri destekler:

  • Başarılı olduğunda çağrılan işlevin değerini döndürür
  • Denemeler biterse çağrılan fonksiyonun istisnasını yükseltir
  • Deneme sayısı sınırı (sınırsız için 0)
  • Denemeler arasında bekleme (doğrusal veya üstel)
  • Yalnızca kural dışı durum belirli bir kural dışı durum türünün örneğiyse yeniden deneyin.
  • İsteğe bağlı girişimlerin günlüğe kaydedilmesi
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

Kullanımı:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

Daha fazla bilgi için yayına bakın .


-2

İşte bunu düzeltmek için benim fikrim:

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

7
Bu üssün dışında.
Chris Johnson

-2

Geçenlerde benim python ile bu soruna bir çözüm üzerinde çalıştı ve ben stackoverflow ziyaretçiler ile paylaşmak için mutluyum gerekirse lütfen geribildirim verin.

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

-9

döngü değişkeninizi yalnızca try deyimi başarılı olduğunda artırın

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.