'बयान' के साथ पायथन का उपयोग करते समय एक अपवाद को पकड़ने


293

मेरी शर्म की बात है, मैं समझ नहीं पा रहा हूं कि 'बयान' के साथ अजगर के लिए अपवाद को कैसे संभालना है। अगर मेरे पास एक कोड है:

with open("a.txt") as f:
    print f.readlines()

मैं वास्तव में somehing करने के लिए 'फ़ाइल को अपवाद नहीं मिला' को संभालना चाहता हूं। लेकिन मैं लिख नहीं सकता

with open("a.txt") as f:
    print f.readlines()
except:
    print 'oops'

और लिख नहीं सकते

with open("a.txt") as f:
    print f.readlines()
else:
    print 'oops'

एक कोशिश में 'के साथ' को छोड़कर / बयान को छोड़कर काम नहीं करता है: अपवाद नहीं उठाया गया है। पायथोनिक तरीके से 'स्टेटमेंट' के साथ 'अंदर' को फेल करने के लिए मैं क्या कर सकता हूं?


क्या मतलब है "एक कोशिश में 'के साथ' संलग्न '/ बयान को छोड़कर काम नहीं करता है: अपवाद नहीं उठाया गया है" ? एक withबयान जादुई रूप से एक आसपास के बयान को नहीं तोड़ता है try...except
अरण-फे

4
दिलचस्प बात यह है कि, जावा के साथ-साथ संसाधनों का कथन आपके उपयोग के मामले में वास्तव में इस समर्थन का समर्थन करता हैdocs.oracle.com/javase/tutorial/essential/exceptions/...
Nayuki

जवाबों:


256
from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'

यदि आप चाहते हैं कि आप जो कार्य कोड कर सकते हैं, वह खुली कॉल से होने वाली त्रुटियों के लिए अलग-अलग हैंडलिंग हो:

try:
    f = open('foo.txt')
except IOError:
    print('error')
else:
    with f:
        print f.readlines()

3
जैसा कि stackoverflow.com/questions/5205811/… में उल्लेख किया गया है , यहाँ कोशिश ब्लॉक वास्तव में बहुत व्यापक है। संदर्भ प्रबंधक और बयान वाले निकाय के सदस्यों को बनाते समय अपवादों के बीच कोई अंतर नहीं किया जाता है, इसलिए यह सभी उपयोग के मामलों के लिए एक वैध समाधान नहीं हो सकता है।
ncoghlan

@ncoghlan लेकिन आप एक अपवाद के स्रोत के करीब होने के लिए अतिरिक्त try...exceptब्लॉक जोड़ सकते हैं withजिसका कोई लेना देना नहीं है open()
rbaleksandar

1
@rbaleksandar यदि मुझे सही से याद है, तो मेरी टिप्पणी उत्तर में पहले उदाहरण का कड़ाई से उल्लेख कर रही थी, जहां कथन के साथ पूरा प्रयास के अंदर है / ब्लॉक को छोड़कर (इसलिए भले ही आपके पास आंतरिक कोशिश / अपेक्षा ब्लॉक हो, किसी भी अपवाद वे बचने देंगे अभी भी बाहरी मारा)। डगलस ने बाद में उन मामलों को संबोधित करने के लिए दूसरा उदाहरण जोड़ा जहां यह अंतर मायने रखता है।
ncoghlan

3
क्या इस उदाहरण में फ़ाइल बंद हो जाएगी? मैं पूछता हूं क्योंकि इसे "के साथ" दायरे से बाहर खोला गया था।
माइक कोलिन्स

6
@MikeCollins 'के साथ' से बाहर निकलने से खुली फ़ाइल तब भी बंद हो जाएगी जब फ़ाइल 'के साथ' से पहले खुली हो।
user7938784

75

withबयान का फायदा उठाने के लिए सबसे अच्छा "पायथोनिक" तरीका, पीईपी 343 में उदाहरण # 6 के रूप में सूचीबद्ध है , जो बयान की पृष्ठभूमि देता है।

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

निम्नानुसार उपयोग किया जाता है:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")

38
मुझे यह पसंद है लेकिन यह थोड़ा बहुत काला जादू जैसा लगता है। इसकी पूरी तरह से पाठक के लिए स्पष्ट नहीं है
पॉल Seeb

5
@PaulSeeb आप इसे परिभाषित क्यों नहीं करेंगे और अपने आप को हर बार ऐसा करने से बचाएंगे जिसकी आपको आवश्यकता है? यह आपके एप्लिकेशन स्तर पर परिभाषित किया गया है, और यह किसी भी अन्य संदर्भदाता के रूप में जादू है। मुझे लगता है कि कथन के साथ कोई व्यक्ति इसे स्पष्ट रूप से समझेगा (यदि आपको यह पसंद नहीं है तो फ़ंक्शन का नाम भी अधिक अभिव्यंजक हो सकता है)। "स्टेटमेंट" के साथ ही इस तरह से काम करने के लिए इंजीनियर किया गया है, कोड के "सुरक्षित" ब्लॉक को परिभाषित करने के लिए और संदर्भ प्रबंधकों को फ़ंक्शन की जाँच करने के लिए (कोड को और अधिक स्पष्ट करने के लिए) कार्य सौंपें।

9
उपयोगकर्ता कोड में अंत में ब्लॉक नहीं लिखने के लिए यह सब परेशानी। मुझे लगता है कि हम सभी को बयान के साथ लंबे समय तक लक्षण के लिए पीड़ित होना शुरू हो रहा है।
jgomo3

1
अजगर में अपवादों को संभालने का सबसे अच्छा तरीका एक फ़ंक्शन लिखना है जो उन्हें पकड़ता है और वापस करता है? गंभीरता से? अपवादों को संभालने के लिए पायथोनिक तरीका एक try...exceptबयान का उपयोग करना है।
अरण-फे

58

'बयान' के साथ पायथन का उपयोग करते समय एक अपवाद को पकड़ने

पायथन 2.6 के बाद से__future__ आयात के बिना बयान उपलब्ध है । आप इसे जल्द से जल्द पाइथन 2.5 के रूप में प्राप्त कर सकते हैं (लेकिन इस समय यह अपग्रेड करने का समय है!):

from __future__ import with_statement

यहाँ सबसे सही चीज़ है जो आपके पास है। आप लगभग वहाँ हैं, लेकिन withएक exceptखंड नहीं है :

with open("a.txt") as f: 
    print(f.readlines())
except:                    # <- with doesn't have an except clause.
    print('oops')

एक संदर्भ प्रबंधक की __exit__विधि, यदि वह रिटर्न करती है Falseतो त्रुटि को समाप्त कर देगी। अगर यह वापस आता है True, तो यह इसे दबा देगा। openअंतर्निहित है __exit__वापस नहीं करता है True, तो आप सिर्फ एक कोशिश में यह घोंसला करने की जरूरत है, ब्लॉक को छोड़कर:

try:
    with open("a.txt") as f:
        print(f.readlines())
except Exception as error: 
    print('oops')

और मानक बॉयलरप्लेट: एक नंगे का उपयोग न करें except:जो पकड़ता है BaseExceptionऔर हर दूसरे संभावित अपवाद और चेतावनी। कम से कम के रूप में के रूप में विशिष्ट हो Exception, और इस त्रुटि के लिए, शायद पकड़ IOError। केवल उन त्रुटियों को पकड़ें जिन्हें आप संभालने के लिए तैयार हैं।

तो इस मामले में, आप क्या करेंगे:

>>> try:
...     with open("a.txt") as f:
...         print(f.readlines())
... except IOError as error: 
...     print('oops')
... 
oops

2

एक यौगिक withबयान से उठाए गए अपवादों की संभावित उत्पत्ति के बीच अंतर करना

एक withबयान में होने वाले अपवादों के बीच अंतर करना मुश्किल है क्योंकि वे विभिन्न स्थानों में उत्पन्न हो सकते हैं। अपवादों को निम्नलिखित स्थानों में से किसी एक से उठाया जा सकता है (या उसमें किए गए कार्य):

  • ContextManager.__init__
  • ContextManager.__enter__
  • का शरीर with
  • ContextManager.__exit__

अधिक जानकारी के लिए संदर्भ प्रबंधक प्रकार के बारे में प्रलेखन देखें ।

अगर हम इन विभिन्न मामलों के बीच अंतर करना चाहते हैं, तो केवल withएक में लपेटना try .. exceptपर्याप्त नहीं है। निम्नलिखित उदाहरण पर विचार करें (उदाहरण के ValueErrorरूप में लेकिन निश्चित रूप से इसे किसी अन्य अपवाद प्रकार के साथ प्रतिस्थापित किया जा सकता है):

try:
    with ContextManager():
        BLOCK
except ValueError as err:
    print(err)

यहां exceptचार अलग-अलग स्थानों में उत्पन्न होने वाले अपवादों को पकड़ा जाएगा और इस तरह उनके बीच अंतर करने की अनुमति नहीं है। हम बाहर संदर्भ प्रबंधक वस्तु की इन्स्टेन्शियशन ले जाते हैं with, हम बीच भेद कर सकते __init__और BLOCK / __enter__ / __exit__:

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        with mgr:
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        # At this point we still cannot distinguish between exceptions raised from
        # __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
        pass

प्रभावी रूप से यह सिर्फ __init__भाग के साथ मदद करता है, लेकिन हम यह जांचने के लिए एक अतिरिक्त संतरी चर जोड़ सकते हैं कि क्या withनिष्पादित करने के लिए शरीर (यानी __enter__और दूसरों के बीच अंतर करना ) है:

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        entered_body = False
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        else:
            # At this point we know the exception came either from BLOCK or from __exit__
            pass

मुश्किल भाग से उत्पन्न होने वाले अपवादों के बीच अंतर करना है BLOCKऔर __exit__क्योंकि एक अपवाद जो withइच्छाशक्ति के शरीर से बच जाता है, उसे पारित किया जाएगा __exit__जो यह तय कर सकता है कि इसे कैसे संभालना है ( डॉक्स देखें )। हालांकि अगर __exit__खुद को उठाता है, तो मूल अपवाद को नए द्वारा बदल दिया जाएगा। इन मामलों से निपटने के लिए हम किसी भी संभावित अपवाद को संग्रहीत करने exceptके withलिए शरीर में एक सामान्य खंड जोड़ सकते हैं जो अन्यथा किसी का ध्यान नहीं भगाएगा और इसकी तुलना सबसे बाहरी exceptबाद में पकड़े गए व्यक्ति से करेगा - यदि वे वही हैं तो इसका मतलब मूल था BLOCKया अन्यथा यह था __exit__(मामले __exit__में बाहरी मान को एक वास्तविक मूल्य लौटाकर अपवाद को दबा देता हैexcept बस निष्पादित नहीं किया जाएगा)।

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    entered_body = exc_escaped_from_body = False
    try:
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
            except Exception as err:  # this exception would normally escape without notice
                # we store this exception to check in the outer `except` clause
                # whether it is the same (otherwise it comes from __exit__)
                exc_escaped_from_body = err
                raise  # re-raise since we didn't intend to handle it, just needed to store it
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        elif err is exc_escaped_from_body:
            print('BLOCK raised:', err)
        else:
            print('__exit__ raised:', err)

पीईपी 343 में उल्लिखित समकक्ष फॉर्म का उपयोग करके वैकल्पिक दृष्टिकोण

पीईपी 343 - "स्टेटमेंट वाला " स्टेटमेंट के बराबर "नॉन-विथ" वर्जन को निर्दिष्ट करता है with। यहाँ हम आसानी से विभिन्न भागों को लपेट सकते हैं try ... exceptऔर इस प्रकार विभिन्न संभावित त्रुटि स्रोतों के बीच अंतर कर सकते हैं:

import sys

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        value = type(mgr).__enter__(mgr)
    except ValueError as err:
        print('__enter__ raised:', err)
    else:
        exit = type(mgr).__exit__
        exc = True
        try:
            try:
                BLOCK
            except TypeError:
                pass
            except:
                exc = False
                try:
                    exit_val = exit(mgr, *sys.exc_info())
                except ValueError as err:
                    print('__exit__ raised:', err)
                else:
                    if not exit_val:
                        raise
        except ValueError as err:
            print('BLOCK raised:', err)
        finally:
            if exc:
                try:
                    exit(mgr, None, None, None)
                except ValueError as err:
                    print('__exit__ raised:', err)

आमतौर पर एक सरल तरीका ठीक काम करेगा

इस तरह के विशेष अपवाद संचालन के लिए की जरूरत काफी दुर्लभ और सामान्य रूप से पूरे लपेटकर होना चाहिए withएक में try ... exceptब्लॉक पर्याप्त होगा। विशेष रूप से यदि विभिन्न त्रुटि स्रोतों को अलग-अलग (कस्टम) अपवाद प्रकारों से दर्शाया जाता है (संदर्भ प्रबंधकों को तदनुसार डिजाइन करने की आवश्यकता है) तो हम आसानी से उनके बीच अंतर कर सकते हैं। उदाहरण के लिए:

try:
    with ContextManager():
        BLOCK
except InitError:  # raised from __init__
    ...
except AcquireResourceError:  # raised from __enter__
    ...
except ValueError:  # raised from BLOCK
    ...
except ReleaseResourceError:  # raised from __exit__
    ...
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.