Python'da bir istisnayı elle yükseltme (atma)


2264

Daha sonra bir exceptblok aracılığıyla yakalanabilmesi için Python'da nasıl bir istisna oluşturabilirim ?

Yanıtlar:


2937

Python'da manuel olarak nasıl bir istisna atarım / yükseltirim?

Sorununuza anlamsal olarak uyan en özel İstisna yapıcısını kullanın .

Mesajınızda açık olun, örneğin:

raise ValueError('A very specific bad thing happened.')

Genel istisnaları gündeme getirmeyin

Jenerik yetiştirmekten kaçının Exception. Onu yakalamak için, onu sınıflandıran diğer tüm özel istisnaları yakalamanız gerekir.

Sorun 1: Hataları gizleme

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Örneğin:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Sorun 2: Yakalamayacak

Ve daha özel yakalamalar genel istisnayı yakalamaz:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

En İyi Uygulamalar: raiseaçıklama

Bunun yerine, sorununuza anlamsal olarak uyan en özel İstisna yapıcısını kullanın .

raise ValueError('A very specific bad thing happened')

ayrıca, kurucuya rasgele sayıda argümanın iletilmesini de sağlar:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Bu bağımsız değişkenlere nesnenin argsözniteliği tarafından erişilir Exception. Örneğin:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

baskılar

('message', 'foo', 'bar', 'baz')    

Python 2.5'te, kullanıcıları İstisnaları alt sınıfa aktarmaya ve kullanmayı bırakmaya teşvik etmek için gerçek bir messageözellik eklendi , ancak argümanların tanıtılması ve orijinal kullanımdan kaldırılması geri çekildi .BaseExceptionargsmessage

En İyi Uygulamalar: exceptyan tümcesi

Dışlama yantümcesinin içindeyken, örneğin, belirli bir hata türünün oluştuğunu günlüğe kaydetmek ve ardından yeniden yükseltmek isteyebilirsiniz. Yığın izini korurken bunu yapmanın en iyi yolu çıplak yükseltme deyimi kullanmaktır. Örneğin:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Hatalarınızı değiştirmeyin ... ama ısrar ediyorsanız.

Yığın izini (ve hata değerini) ile koruyabilirsiniz sys.exc_info(), ancak bu daha fazla hataya yatkındır ve Python 2 ve 3 arasında uyumluluk sorunları vardır , raiseyeniden yükseltmek için çıplak kullanmayı tercih edin .

Açıklamak için - sys.exc_info()tür, değer ve geri izleme döndürür.

type, value, traceback = sys.exc_info()

Bu Python 2'deki sözdizimidir - bunun Python 3 ile uyumlu olmadığını unutmayın:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

İsterseniz, yeni yükseltmenizde ne olacağını değiştirebilirsiniz - örneğin args, örnek için yeni ayar yapmak:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

Argleri değiştirirken tüm izleri koruduk. Bunun en iyi uygulama olmadığını ve Python 3'te geçersiz sözdizimi olduğunu unutmayın (uyumluluğun daha fazla çalışmasını zorlaştırır).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

In Python 3 :

    raise error.with_traceback(sys.exc_info()[2])

Tekrar: Geri izlemeleri manuel olarak manipüle etmekten kaçının. Daha az verimli ve hataya daha yatkındır. Eğer diş çekme kullanıyorsanız ve sys.exc_infoyanlış geri izleme bile alabilirsiniz (özellikle kontrol akışı için istisna işleme kullanıyorsanız - ki bu şahsen kaçınmaya eğilimliydim.)

Python 3, İstisna zinciri

Python 3'te, geri izleri koruyan İstisnaları zincirleyebilirsiniz:

    raise RuntimeError('specific message') from error

Farkında olmak:

  • bu , yükseltilen hata türünün değiştirilmesine izin verir ve
  • bu Python 2 ile uyumlu değildir .

Kullanımdan Kaldırılan Yöntemler:

Bunlar kolayca gizlenebilir ve hatta üretim koduna girebilir. Bir istisna oluşturmak istiyorsunuz ve bunları yapmak bir istisna yaratacaktır, ancak amaçlananı değil!

Python 2'de geçerlidir, ancak Python 3'te geçerli değildir :

raise ValueError, 'message' # Don't do this, it's deprecated!

Sadece Python'un (2.4 ve daha eski) çok eski sürümlerinde geçerlidir , yine de dizeleri yükselten insanları görebilirsiniz:

raise 'message' # really really wrong. don't do this.

Tüm modern versiyonlarda, bu aslında bir artıracaktır TypeError, çünkü bir BaseExceptiontür yetiştirmiyorsunuz . Doğru istisnayı kontrol etmiyorsanız ve sorunun farkında olan bir yorumcunuz yoksa, üretime geçebilir.

Örnek Kullanım

Yanlış kullandıkları takdirde tüketicilerimi API'm konusunda uyarmak için İstisnalar oluşturuyorum:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Uygun olduğunda kendi hata türlerinizi oluşturun

"Amaç dışında bir hata yapmak istiyorum, böylece dışına girecek"

Kendi hata türlerinizi oluşturabilirsiniz, uygulamanızla ilgili belirli bir şeyin yanlış olduğunu belirtmek istiyorsanız, istisna hiyerarşisinde uygun noktayı alt sınıfta tutmanız yeterlidir:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

ve kullanım:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

19
Bunun için teşekkürler, tam da ihtiyacım olan şey bu. Çıplak raiseben yığın izini bozmadan kod yürütme birden çok düzeyde özel hata ayıklama gerçekleştirmek için gereken şeydir.
CaffeineConnoisseur

Bu harika bir cevap. Ama hala 2.7 kod çok çalışıyorum ve sık sık kendimi bir giriş dosyası konumu veya bazı değişkenlerin değerleri gibi beklenmedik bir istisna bilgi eklemek isteyen bulmak, ancak orijinal yığın ve istisna tutmak. Günlüğe kaydedebilirim, ancak bazen günlüğe kaydedilmesini istemiyorum, örneğin üst kod sonuçta işliyorsa. raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]istediğimi yapıyor gibi görünüyor ve ben onunla hiçbir zaman sorunla karşılaşmadım. Ama kabul edilebilir bir uygulama değil, acayip hissediyor. Daha iyi bir yol var mı?
Michael Scheper

2
@brennanyoung Bu bağlamda bir SyntaxError yükseltmenin kafa karıştırıcı olabileceğini düşünüyorum - muhtemelen özel bir istisna oluşturmalısınız. Nasıl yapılacağını açıklarım: stackoverflow.com/a/26938914/541136
Aaron Hall

2
Tam teklifin "Tüm yerleşik, sistemden çıkmayan istisnalar bu sınıftan türetildiğini unutmayın. Kullanıcı tanımlı tüm istisnalar da bu sınıftan türetilmelidir". - Bu, çoğunlukla Exceptionana sınıfınız olarak türetilmeyen 4 istisnadan birini kullanmamanız gerektiği anlamına gelir - daha spesifik bir şeyi alt sınıflandırabilirsiniz ve eğer mantıklıysa bunu yapmalısınız.
Aaron Hall

1
" En İyi Uygulamalar: yan tümcesi " örneğinde , tanımlanmamış bir AppErroristisna kullanırsınız. Gibi yerleşik bir hata kullanmak daha iyi olabilirAttributeError
Stevoisiak

530

BUNU YAPMAYIN . Çıplak yetiştirmek Exceptionkesinlikle doğru bir şey değildir ; Aaron Hall'ın mükemmel cevabına bakın .

Bundan daha fazla pitonik olamaz:

raise Exception("I know python!")

Daha fazla bilgi istiyorsanız, python için zam bildirimi belgelerine bakın .


67
Hayır lütfen! Bu, yakaladığınız şeyle ilgili spesifik olma potansiyelini ortadan kaldırır. Tamamen yanlış bir yol. Bunun yerine Aaron Hall'un mükemmel cevabına bir göz atın. Keşke cevap başına birden fazla aşağı oy verebilseydim.
Dawood ibn Kareem

27
@PeterR Bu kadar az oyu olması aynı derecede korkunç. Bu cevabı okuyan HERKES için, BU ŞEYİ YAPMAYIN! Doğru cevap Aaron Hall'ın cevabı.
Dawood ibn Kareem

6
Bunun neden yanlış veya çok kötü olduğuna dair daha ayrıntılı bir açıklama olması gerektiğini düşünüyorum.
Charlie Parker

9
@CharlieParker Var. Aaron Hall'ın cevabının ilk kısmı .
Dinei

5
Bu cevap neden silinmek üzere işaretlenemiyor? Şimdiden 93 downvotes var!
codeforester

54

Python3'te istisnaları istisna etmek için 4 farklı sözdizimi vardır:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. istisna uyandırmak vs. 2. istisna ulaştırmak (argümanlar)

Bir raise exception (args) istisnayı yükseltmek için argskullanırsanız, istisna nesnesini yazdırdığınızda bu istisna yazdırılır - aşağıdaki örnekte gösterildiği gibi.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raiseherhangi bir argüman olmadan ifade son istisnayı yeniden gündeme getirir. Bu, istisnayı yakaladıktan sonra bazı eylemleri gerçekleştirmeniz ve ardından yeniden yükseltmek istiyorsanız kullanışlıdır. Ancak daha önce bir istisna yoksa, raiseifade İstisna'yı yükseltir TypeError.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. original_exception istisnasını (argümanlar) yükseltin

Bu ifade, başka bir istisnaya yanıt olarak ortaya konan bir istisnanın, aşağıdaki istisnada gösterildiği gibi orijinal istisnanın ayrıntılarını içerebileceği istisna zincirlemesi oluşturmak için kullanılır.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Çıktı:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

7
Not PEP8 tercih edin o exception(args)aşkınexception (args)
Gloweye

Ayrıca raise exception(args) from Noneşu anda aktif olan istisnanın ele alındığını ve artık ilgilenilmediğini söylemek gerekir. Aksi takdirde, bir exceptblok içinde bir istisna yükseltir ve işlenmezse, her iki istisna için izlemeler, “Yukarıdaki istisnanın işlenmesi sırasında başka bir istisna oluştu” mesajıyla ayrılmış olarak gösterilir
cg909

35

Bazı beklenmedik koşullara yanıt olarak bir istisna atmanız gereken ve asla yakalamayı düşünmediğiniz, ancak o zaman hata ayıklamanıza izin vermek için hızlı bir şekilde başarısız olmanız gereken en yaygın durum - en mantıklı olan gibi görünüyor AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

19
Bu, bir iddiada sorun olmadığı için (burada hiçbiri yapılmadığı için) ValueErrordaha iyi bir durumdur AssertionError- sorun bir değerde. AssertionErrorBu durumda gerçekten bir istiyorsanız , yazın assert distance > 0, 'Distance must be positive'. Ancak bu şekilde hata kontrol etmemelisiniz çünkü iddialar kapatılabilir ( python -O).
İki Bit Simyacı

1
@ İki BitAlchemist İyi bir nokta. Yukarıdaki basit örneği yazdığımda, basitleştirme fikri kayboldu. Birçok benzer durumda, belirli bir değerle ilişkili olmayan bir durumdur. Aksine, "kontrol akışı asla buraya gelmemelidir" anlamındadır.
Evgeni Sergeev

2
@ Two-BitAlchemist İddiaları kapatılabilir, evet, ancak hata kontrolü için bunları kullanmamalısınız?
Evgeni Sergeev

Şey bağlıdır. Dağıtmak istediğim bir programdaki tek hata kontrolüm olmasına izin vermezdim. Öte yandan, sadece iş arkadaşlarım için bir program hazırlayabilir ve onlarla birlikte çalıştırırlarsa, bunları kendi riskleriyle kullandıklarını söyleyebilirim -O.
İki Bit Simyacı

1
@ Two-BitAlchemist Benim için iddiaların rolü kendi başına hata kontrolü değil (testin ne için olduğu), ancak kod içinde bazı hataların geçemediği çitler kurdu. Böylece kaçınılmaz olarak meydana gelecek olan hataları izlemek ve izole etmek daha kolay hale gelir. Bu çok az çaba gerektiren iyi alışkanlıklar, test etmek ise çok çaba ve çok zaman alıyor.
Evgeni Sergeev

12

Önce mevcut cevapları okuyun, bu sadece bir zeyilname.

Bağımsız değişkenli veya bağımsız değişkenli özel durumlar oluşturabileceğinize dikkat edin.

Misal:

raise SystemExit

programdan çıkar ancak ne olduğunu bilmek isteyebilirsiniz.

raise SystemExit("program exited")

program kapatılmadan önce stderr'a "programdan çıkıldı" yazdıracaktır.


2
Bu OOP paradigmasına karşı değil mi? Birinci durumun sınıf başvurusunu, ikincisinin ise SystemExit örneğini attığını varsayıyorum. Daha raise SystemExit()iyi bir seçim olmaz mıydı? İlki neden çalışıyor?
burny

2

İstisnalar atmanın bir başka yolu da assert. Bir koşulun yerine getirilip getirilmediğini doğrulamak için assert kullanabilirsiniz AssertionError. Daha fazla ayrıntı için buraya bir göz atın .

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

2

Sadece şunu not edin: Genel istisnaları işlemek istediğiniz zamanlar vardır. Bir grup dosyayı işliyorsanız ve hatalarınızı kaydediyorsanız, bir dosyada oluşan herhangi bir hatayı yakalamak, günlüğe kaydetmek ve diğer dosyaları işlemeye devam etmek isteyebilirsiniz. Bu durumda,

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

yapmak için iyi bir yol engeller. Yine de raisebelirli istisnalar isteyeceksiniz, böylece ne anlama geldiğini biliyorsunuz.


0

Bunun için python zam ifadesini öğrenmelisiniz. Deneme bloğunun içinde tutulmalıdır. Misal -

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
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.