क्या 'अंत' हमेशा पायथन में निष्पादित होता है?


126

पायथन में किसी भी संभावित कोशिश-अंत में, क्या यह गारंटी है कि finallyब्लॉक को हमेशा निष्पादित किया जाएगा?

उदाहरण के लिए, मान लें कि मैं किसी exceptब्लॉक में रहते हुए लौटता हूं :

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

या हो सकता है कि मैं फिर से उठाऊं Exception:

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

परीक्षण से पता चलता है कि finallyउपरोक्त उदाहरणों के लिए निष्पादित किया जाता है, लेकिन मुझे लगता है कि ऐसे अन्य परिदृश्य हैं जिनके बारे में मैंने नहीं सोचा है।

क्या ऐसे कोई परिदृश्य हैं जिनमें एक finallyब्लॉक पायथन में निष्पादित करने में विफल हो सकता है?


18
एकमात्र ऐसा मामला जिसकी मैं कल्पना कर सकता हूं कि वह finallyनिष्पादित या "अपने उद्देश्य को हराने" में एक अनन्त लूप, sys.exitया एक मजबूर व्यवधान के दौरान विफल है । प्रलेखन कहा गया है कि finallyहमेशा निष्पादित किया जाता है, तो मैं उस के साथ जाना चाहते हैं।
Xay

1
थोड़ी सी सोच और यह सुनिश्चित करें कि आपने क्या पूछा है, लेकिन मुझे पूरा यकीन है कि यदि आप टास्क मैनेजर खोलते हैं और प्रक्रिया को मारते हैं, finallyतो नहीं चलेगा। या उसी तरह अगर कंप्यूटर पहले दुर्घटनाग्रस्त हो जाता है: D
अलेजांद्रो

144
finallyपावर कॉर्ड दीवार से फट गया है, तो निष्पादित नहीं करेगा।
user253751


1
इसे खाली सेमाफ़ोर पर ब्लॉक करें। इसका संकेत कभी न दें। किया हुआ।
मार्टिन जेम्स

जवाबों:


206

"गारंटीड" किसी भी कार्यान्वयन के finallyयोग्य से अधिक मजबूत शब्द है । क्या गारंटी है कि अगर निष्पादन पूरे से बाहर बहती है try- finallyनिर्माण, यह से होकर गुजरेगी finallyऐसा करने के लिए। क्या गारंटी नहीं है कि निष्पादन से बाहर हो जाएगा try- finally

  • एक finallyजनरेटर या async coroutine में कभी नहीं चल सकता है , अगर ऑब्जेक्ट कभी भी निष्कर्ष पर नहीं जाता है। बहुत सारे तरीके हैं जो हो सकते हैं; यहां एक है:

    def gen(text):
        try:
            for line in text:
                try:
                    yield int(line)
                except:
                    # Ignore blank lines - but catch too much!
                    pass
        finally:
            print('Doing important cleanup')
    
    text = ['1', '', '2', '', '3']
    
    if any(n > 1 for n in gen(text)):
        print('Found a number')
    
    print('Oops, no cleanup.')
    

    ध्यान दें कि यह उदाहरण थोड़ा मुश्किल है: जब जनरेटर कचरा एकत्र किया जाता है, तो पायथन finallyएक GeneratorExitअपवाद में फेंककर ब्लॉक को चलाने का प्रयास करता है, लेकिन यहां हम उस अपवाद को पकड़ते हैं और फिर yieldसे, जिस बिंदु पर पायथन एक चेतावनी प्रिंट करता है ("जनरेटर ने जनरेटर जेनरेट किया है) ”) और देता है। विवरण के लिए PEP 342 (संवर्धित जनरेटरों के माध्यम से Coroutines) देखें।

    अन्य तरीके जनरेटर या coroutine निष्कर्ष पर अमल नहीं हो सकता है या अगर एक में शामिल हैं, अगर वस्तु सिर्फ GC'ed कभी नहीं किया गया है (हाँ, संभव है, CPython में भी) async with awaitमें __aexit__, या अगर वस्तु awaitया yieldएक में है finallyब्लॉक। यह सूची संपूर्ण नहीं है।

  • finallyडीमन थ्रेड में ए कभी भी निष्पादित नहीं हो सकता है यदि सभी गैर-डेमन थ्रेड पहले बाहर निकलते हैं।

  • os._exitfinallyब्लॉक निष्पादित किए बिना प्रक्रिया को तुरंत रोक देगा

  • os.forkदो बार निष्पादितfinally करने के लिए ब्लॉक का कारण हो सकता है । साथ ही बस सामान्य समस्याएं जो आप दो बार होने वाली चीजों से उम्मीद करेंगे, इससे समवर्ती पहुंच संघर्ष (क्रैश, स्टॉल, ...) हो सकता है यदि साझा संसाधनों तक पहुंच सही ढंग से सिंक्रनाइज़ नहीं है

    चूंकि कांटा प्रारंभ विधि (यूनिक्स पर डिफ़ॉल्ट) multiprocessingका उपयोग करते समय कार्यकर्ता प्रक्रियाओं को बनाने के लिए कांटा-बिना-निष्पादन का उपयोग करता है , और फिर कार्यकर्ता की नौकरी होने के बाद कार्यकर्ता में कॉल करता है, और बातचीत समस्याग्रस्त ( उदाहरण ) हो सकती है ।os._exitfinallymultiprocessing

  • सी-लेवल सेगमेंटेशन फॉल्ट finallyब्लॉक को चलने से रोक देगा ।
  • kill -SIGKILLfinallyब्लॉक को चलने से रोक देगा । SIGTERMऔर जब तक आप स्वयं शटडाउन को नियंत्रित करने के लिए एक हैंडलर स्थापित नहीं करते हैं, तब तक ब्लॉक को चलने से SIGHUPरोका जाएगा finally; डिफ़ॉल्ट रूप से, पायथन संभालता नहीं है SIGTERMया SIGHUP
  • एक अपवाद finallyसफाई को पूरा करने से रोक सकता है। एक विशेष रूप से उल्लेखनीय मामला है, अगर उपयोगकर्ता नियंत्रण-सी को हिट करता है, जैसे कि हम finallyब्लॉक को निष्पादित करना शुरू कर रहे हैं । पायथन ब्लॉक की सामग्री KeyboardInterruptकी प्रत्येक पंक्ति को बढ़ाएगा और छोड़ देगा finally। ( KeyboardInterrupt-safe code लिखना बहुत कठिन है)।
  • यदि कंप्यूटर बिजली खो देता है, या यदि वह हाइबरनेट करता है और नहीं उठता है, तो finallyब्लॉक नहीं चलेंगे।

finallyब्लॉक एक सौदे प्रणाली नहीं है, यह एटोमिसिटी गारंटी या किसी भी प्रकार का कुछ भी प्रदान नहीं करता है। इन उदाहरणों में से कुछ स्पष्ट लग सकता है, लेकिन ऐसी चीजों को भूलना आसान हो सकता है और finallyबहुत अधिक के लिए भरोसा कर सकता है।


14
मेरा मानना ​​है कि आपकी सूची का केवल पहला बिंदु वास्तव में प्रासंगिक है, और इससे बचने का एक आसान तरीका है: 1) कभी भी एक नंगे का उपयोग न करें except, और कभी भी GeneratorExitजनरेटर के अंदर न पकड़ें। थ्रेड्स / प्रोसेस को मारने / सेगफॉल्टिंग / पावर ऑफ के बारे में बिंदु अपेक्षित हैं, अजगर जादू नहीं कर सकता। इसके अलावा: अपवाद finallyस्पष्ट रूप से एक समस्या है, लेकिन यह इस तथ्य को नहीं बदलता है कि नियंत्रण प्रवाह कोfinally ब्लॉक में स्थानांतरित किया गया था । के बारे में Ctrl+C, आप एक सिग्नल हैंडलर जोड़ सकते हैं जो इसे अनदेखा करता है, या बस वर्तमान ऑपरेशन पूरा होने के बाद एक साफ शटडाउन "शेड्यूल" करता है।
जियाको अल्जेटा

8
किल -9 का उल्लेख तकनीकी रूप से सही है, लेकिन थोड़ा अनुचित है। किसी भी भाषा में लिखा गया कोई प्रोग्राम किल -9 प्राप्त करने पर कोई कोड नहीं चलाता है। वास्तव में, कोई भी कार्यक्रम कभी भी एक किल -9 प्राप्त नहीं करता है, इसलिए यहां तक ​​कि अगर यह चाहता था, तो यह कुछ भी निष्पादित नहीं कर सकता था। यही मार -9 की पूरी बात है।
टॉम

10
@ टिप्पणी: kill -9एक भाषा के बारे में बिंदु निर्दिष्ट नहीं किया गया। और स्पष्ट रूप से, इसे दोहराने की आवश्यकता है, क्योंकि यह एक अंधे स्थान पर बैठता है। बहुत से लोग भूल जाते हैं, या महसूस नहीं करते हैं, कि उनके कार्यक्रम को साफ किए बिना भी इसकी पटरियों में मृत रोका जा सकता है।
cHao

5
@GiacomoAlzetta: वहाँ के लोग finallyब्लॉक पर भरोसा कर रहे हैं मानो उन्होंने लेन-देन की गारंटी दी हो। यह स्पष्ट प्रतीत हो सकता है कि वे नहीं करते हैं, लेकिन यह ऐसा कुछ नहीं है जिसे हर कोई महसूस करता है। जनरेटर मामले के लिए, बहुत सारे तरीके हैं एक जनरेटर GC'ed बिल्कुल भी नहीं हो सकता है, और बहुत सारे तरीके जनरेटर या कोरटाइन गलती से उपज सकते हैं, GeneratorExitभले ही यह पकड़ न हो GeneratorExit, उदाहरण के लिए यदि कोई async withनिलंबित करता है में एक coroutine __exit__
user2357112

2
@ user2357112 हाँ - मैं कई सालों से कोशिश कर रहा हूं कि ऐप स्टार्टअप पर टेंप फाइल्स आदि साफ करने के लिए देवें, बाहर न निकलें। तथाकथित 'क्लीन अप एंड ग्रेसफुल टर्मिनेशन' पर भरोसा करते हुए निराशा और आंसू बहा रहे हैं :)
मार्टिन जेम्स

68

हाँ। अंत में हमेशा जीतता है।

पराजित करने का एकमात्र तरीका निष्पादन को रोकना है जिससे पहले finally:निष्पादित करने का मौका मिलता है (जैसे दुभाषिया को क्रैश करना, अपने कंप्यूटर को बंद करना, एक जनरेटर को हमेशा के लिए निलंबित करना)।

मुझे लगता है कि ऐसे अन्य परिदृश्य हैं जिनके बारे में मैंने नहीं सोचा है।

यहाँ कुछ और हैं जिनके बारे में आपने नहीं सोचा होगा:

def foo():
    # finally always wins
    try:
        return 1
    finally:
        return 2

def bar():
    # even if he has to eat an unhandled exception, finally wins
    try:
        raise Exception('boom')
    finally:
        return 'no boom'

आप दुभाषिया को कैसे छोड़ते हैं, इसके आधार पर, कभी-कभी आप अंततः "रद्द" कर सकते हैं, लेकिन इस तरह नहीं:

>>> import sys
>>> try:
...     sys.exit()
... finally:
...     print('finally wins!')
... 
finally wins!
$

अनिश्चित का उपयोग करते हुए os._exit(यह मेरी राय में "दुभाषिया को क्रैश करें" के तहत आता है):

>>> import os
>>> try:
...     os._exit(1)
... finally:
...     print('finally!')
... 
$

मैं वर्तमान में इस कोड को चला रहा हूं, यह परखने के लिए कि आखिरकार ब्रह्मांड की गर्मी से मृत्यु के बाद भी क्या होगा:

try:
    while True:
       sleep(1)
finally:
    print('done')

हालाँकि, मैं अभी भी परिणाम पर प्रतीक्षा कर रहा हूं, इसलिए बाद में यहां वापस देखें।


5
या कोशिश में पकड़ने में एक i परिमित लूप होना
चिकित्सा

8
finallyएक जनरेटर या कोरटाइन में आसानी से निष्पादित करने में विफल हो सकते हैं , "दुर्घटना दुभाषिया" स्थिति के पास कहीं भी जाने के बिना।
user2357112

27
ब्रह्मांड की गर्मी की मृत्यु के बाद अस्तित्व समाप्त हो जाता है, इसलिए sleep(1)निश्चित रूप से अपरिभाषित व्यवहार होगा। :-D
डेविड फ़ॉस्टर

आप सीधे "संकलक को क्रैश करने के लिए इसे हराने का एकमात्र तरीका" के बाद _os.exit का उल्लेख करना चाह सकते हैं। अभी यह इनबेटिवीन उदाहरणों में मिश्रित है जहां अंत में जीत होती है।
स्टेवोइसाक

2
@StevenVascellaro मुझे नहीं लगता कि यह आवश्यक है - os._exitसभी व्यावहारिक उद्देश्यों के लिए, क्रैश को प्रेरित करने (अपवित्र निकास) के समान है। बाहर निकलने का सही तरीका है sys.exit
विम

9

पायथन प्रलेखन के अनुसार :

कोई फर्क नहीं पड़ता कि पहले क्या हुआ था, कोड ब्लॉक पूरा होने के बाद अंतिम ब्लॉक को निष्पादित किया जाता है और किसी भी अपवाद को संभाला जाता है। यहां तक ​​कि अगर अपवाद हैंडलर या अन्य-ब्लॉक में कोई त्रुटि है और एक नया अपवाद उठाया जाता है, तो अंतिम-ब्लॉक में कोड अभी भी चलाया जाता है।

यह भी ध्यान दिया जाना चाहिए कि यदि अंत में ब्लॉक में एक सहित कई रिटर्न स्टेटमेंट हैं, तो अंत में ब्लॉक रिटर्न एकमात्र ऐसा है जो निष्पादित करेगा।


8

खैर, हाँ और नहीं।

क्या गारंटी है कि पायथन हमेशा अंततः ब्लॉक को निष्पादित करने की कोशिश करेगा। उस स्थिति में जहां आप ब्लॉक से लौटते हैं या एक अनकहा अपवाद को बढ़ाते हैं, अंत में ब्लॉक को वास्तव में लौटने या अपवाद को बढ़ाने से ठीक पहले निष्पादित किया जाता है।

(क्या आप अपने सवाल में कोड चलाकर खुद को नियंत्रित कर सकते थे)

एकमात्र मामला जिसकी मैं कल्पना कर सकता हूं कि आखिरकार ब्लॉक को कैसे निष्पादित नहीं किया जाएगा, जब पायथन दुभाषिया स्वयं सी कोड के अंदर या पावर आउटेज के कारण दुर्घटनाग्रस्त हो जाता है।


हा हा .. या
ट्रैप

मुझे लगता है कि "ठीक है, हाँ और नहीं" सबसे सही है। अंत में: हमेशा जीतता है जहां "हमेशा" का अर्थ है कि दुभाषिया चलाने में सक्षम है और "अंत:" के लिए कोड अभी भी उपलब्ध है, और "जीत" को परिभाषित किया जाता है क्योंकि दुभाषिया आखिरकार चलाने की कोशिश करेगा: ब्लॉक और यह सफल होगा। यह "हाँ" है और यह बहुत ही सशर्त है। "नहीं" सभी तरीके हैं जो दुभाषिया "अंत में" से पहले बंद हो सकते हैं: - बिजली की विफलता, हार्डवेयर की विफलता, दुभाषिया के उद्देश्य से मार -9, दुभाषिया या कोड में त्रुटियों पर निर्भर करता है, दुभाषिया को लटकाने के अन्य तरीके। और "आखिरकार:" के अंदर लटकने के तरीके।
बिल IV

1

मैंने जनरेटर फ़ंक्शन का उपयोग किए बिना इसे पाया:

import multiprocessing
import time

def fun(arg):
  try:
    print("tried " + str(arg))
    time.sleep(arg)
  finally:
    print("finally cleaned up " + str(arg))
  return foo

list = [1, 2, 3]
multiprocessing.Pool().map(fun, list)

नींद किसी भी कोड हो सकती है जो असंगत मात्रा में समय के लिए चल सकती है।

यहाँ ऐसा प्रतीत होता है कि ट्रायल ब्लॉक को समाप्त करने की पहली समानांतर प्रक्रिया सफलतापूर्वक समाप्त हो जाती है, लेकिन फिर फ़ंक्शन से ऐसे मान (फू) से लौटने का प्रयास किया जाता है जिसे कहीं भी परिभाषित नहीं किया गया है, जो अपवाद का कारण बनता है। यह अपवाद अन्य प्रक्रियाओं को उनके अंतिम ब्लॉकों तक पहुंचने की अनुमति के बिना मानचित्र को मारता है।

इसके अलावा, यदि आप bar = bazzनींद के ठीक बाद लाइन जोड़ते हैं () कोशिश ब्लॉक में कॉल करें। फिर उस लाइन तक पहुंचने की पहली प्रक्रिया एक अपवाद को फेंक देती है (क्योंकि bazz को परिभाषित नहीं किया गया है), जिसके कारण उसका अपना ब्लॉक अंततः चलाया जा सकता है, लेकिन फिर मैप को मारता है, जिससे दूसरे ट्रायल ब्लॉक उनके अंतिम ब्लॉकों तक पहुंच के बिना गायब हो जाते हैं, और पहली प्रक्रिया अपने रिटर्न स्टेटमेंट तक नहीं पहुंच पाती है।

पायथन मल्टीप्रोसेसिंग के लिए इसका मतलब यह है कि आप सभी प्रक्रियाओं में संसाधनों को साफ करने के लिए अपवाद-हैंडलिंग तंत्र पर भरोसा नहीं कर सकते हैं, अगर प्रक्रियाओं में से एक का भी अपवाद हो सकता है। मल्टीप्रोसेसिंग मैप कॉल के बाहर संसाधनों को संभालने या प्रबंधित करने के लिए अतिरिक्त सिग्नल की व्यवस्था आवश्यक होगी।


-2

स्वीकृत उत्तर के लिए परिशिष्ट, बस यह देखने के लिए कि यह कैसे काम करता है, कुछ उदाहरणों के साथ:

  • यह:

     try:
         1
     except:
         print 'except'
     finally:
         print 'finally'

    उत्पादन होगा

    आखिरकार

  •    try:
           1/0
       except:
           print 'except'
       finally:
           print 'finally'

    उत्पादन होगा


    अंत में छोड़कर


1
यह प्रश्न का उत्तर नहीं देता है। इसके उदाहरण हैं कि यह कब काम करता है
ज़िकुआन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.