अजगर एक अच्छा प्रोग्रामिंग अभ्यास में ब्लॉक को छोड़कर / नेस्टेड हैं?


201

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

dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo

मुझे पता है कि इस तरह से कुछ लिखना बेवकूफी हो सकती है, लेकिन यह है कि मुझे जो कार्यक्षमता प्रदान करनी होगी। मैं इसे निम्नलिखित तरीके से लागू करने के बारे में सोच रहा था:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        try:
            return self.dict[item]
        except KeyError:
            print "The object doesn't have such attribute"

मुझे यकीन नहीं है कि नेस्टेड प्रयास को छोड़कर / ब्लॉक को छोड़कर एक अच्छा अभ्यास है, इसलिए एक और तरीका उपयोग करना होगा hasattr()और has_key():

def __getattribute__(self, item):
        if hasattr(self, item):
            return object.__getattribute__(item)
        else:
            if self.dict.has_key(item):
                return self.dict[item]
            else:
                raise AttributeError("some customised error")

या उनमें से एक का उपयोग करने के लिए और इस तरह से एक ब्लॉक पकड़ने की कोशिश करें:

def __getattribute__(self, item):
    if hasattr(self, item):
        return object.__getattribute__(item)
    else:
        try:
            return self.dict[item]
        except KeyError:
            raise AttributeError("some customised error")

सबसे अजगर और सुरुचिपूर्ण कौन सा विकल्प है?


अजगर देवताओं की सराहना करेंगे if 'foo' in dict_container:। तथास्तु।
gseattle

जवाबों:


180

आपका पहला उदाहरण पूरी तरह से ठीक है। यहां तक ​​कि आधिकारिक पायथन डॉक्स इस शैली को ईएएफपी के रूप में जाना जाता है ।

निजी तौर पर, जब आवश्यक नहीं हो तो मैं घोंसले से बचना पसंद करता हूं:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass  # fallback to dict
    try:
        return self.dict[item]
    except KeyError:
        raise AttributeError("The object doesn't have such attribute") from None

पुनश्च। has_key()पाइथन 2 में लंबे समय से पदावनत किया गया है item in self.dict। इसके बजाय उपयोग करें ।


2
return object.__getattribute__(item)गलत है और उत्पादन करेगा TypeErrorक्योंकि गलत संख्या में तर्क पारित किए जा रहे हैं। इसके बजाय होना चाहिए return object.__getattribute__(self, item)
1

13
पीईपी 20: फ्लैट नेस्टेड से बेहतर है।
Ioannis Filippidis

7
from Noneअंतिम पंक्ति में क्या मतलब है?
निकल्स

2
@ मिनलस यह अनिवार्य रूप से अपवाद संदर्भ को दबा देता है ("इस अपवाद को संभालने के दौरान एक और अपवाद घटित हुआ" -सेक संदेश)। यहाँ
Kade

तथ्य यह है कि पायथन डॉक्स घोंसले के शिकार की कोशिश कर रहा है पागल की तरह है। यह स्पष्ट रूप से भयावह शैली है। संचालन की एक श्रृंखला को संभालने का सही तरीका जहां चीजें विफल हो सकती हैं जैसे कि किसी प्रकार के राक्षसी निर्माण का उपयोग करना होगा, जो पायथन का समर्थन नहीं करता है।
हेनरी हेनरिंसन

19

जबकि जावा में वास्तव में प्रवाह नियंत्रण के लिए अपवादों का उपयोग करने के लिए एक बुरा अभ्यास है (मुख्यतः क्योंकि अपवाद संसाधनों को इकट्ठा करने के लिए जेवीएम को मजबूर करते हैं ( अधिक यहां ), पायथन में आपके पास 2 महत्वपूर्ण सिद्धांत हैं: डक टाइपिंग और ईएएफपी । इसका मूल रूप से मतलब है कि आपको एक वस्तु का उपयोग करने का प्रयास करने के लिए प्रोत्साहित किया जाता है जिस तरह से आपको लगता है कि यह काम करेगा, और जब चीजें इस तरह से न हों, तो संभाल लें।

संक्षेप में एकमात्र समस्या यह होगी कि आपका कोड बहुत ज्यादा इंडेंट हो रहा होगा। यदि आपको ऐसा लगता है, तो सुझाए गए कुछ घोंसले जैसे कि घोंसले को सरल बनाने का प्रयास करें


10

अपने विशिष्ट उदाहरण के लिए, आपको वास्तव में उन्हें घोंसले बनाने की आवश्यकता नहीं है। यदि tryब्लॉक में अभिव्यक्ति सफल होती है, तो फ़ंक्शन वापस आ जाएगा, इसलिए पूरी कोशिश के बाद / ब्लॉक को छोड़कर कोई भी कोड केवल तभी चलाया जाएगा जब पहला प्रयास विफल हो जाता है। तो आप बस कर सकते हैं:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass
    # execution only reaches here when try block raised AttributeError
    try:
        return self.dict[item]
    except KeyError:
        print "The object doesn't have such attribute"

उन्हें घोंसला बनाना बुरा नहीं है, लेकिन मुझे ऐसा लगता है कि इसे सपाट छोड़ने से संरचना अधिक स्पष्ट हो जाती है: आप क्रमिक रूप से चीजों की एक श्रृंखला की कोशिश कर रहे हैं और पहले वाले काम पर लौट रहे हैं।

संयोग से, आप सोच सकते हैं कि क्या आप वास्तव __getattribute__में __getattr__यहां के बजाय उपयोग करना चाहते हैं। उपयोग करना __getattr__चीजों को सरल करेगा क्योंकि आपको पता चल जाएगा कि सामान्य विशेषता लुकअप प्रक्रिया पहले ही विफल हो गई है।


10

बस सावधान रहें - इस मामले में सबसे पहले finallyBUT को भी छुआ गया है।

def a(z):
    try:
        100/z
    except ZeroDivisionError:
        try:
            print('x')
        finally:
            return 42
    finally:
        return 1


In [1]: a(0)
x
Out[1]: 1

वाह यह मेरे दिमाग को उड़ा देता है ... क्या आप मुझे इस व्यवहार की व्याख्या करते हुए कुछ दस्तावेजीकरण के बिंदु पर बता सकते हैं?
मचल

1
@ मिचल: फ़ि: दोनों finallyब्लॉकों के लिए निष्पादित किया जाता है a(0), लेकिन केवल माता-पिता finally-returnको लौटा दिया जाता है।
स्लावोमिर लेनार्ट

7

मेरी राय में, इसे संभालने का सबसे पाइथोनिक तरीका होगा, हालाँकि और क्योंकि यह आपके सवाल को गलत बनाता है। ध्यान दें कि यह परिभाषित __getattr__()करने के बजाय __getattribute__()ऐसा करने का मतलब है कि इसे केवल आंतरिक शब्दकोश में "विशेष" विशेषताओं के साथ व्यवहार करना है।

def __getattr__(self, name):
    """only called when an attribute lookup in the usual places has failed"""
    try:
        return self.my_dict[name]
    except KeyError:
        raise AttributeError("some customized error message")

2
ध्यान दें कि एक exceptब्लॉक में एक अपवाद बढ़ाने से पाइथन 3 में भ्रामक आउटपुट मिल सकता है। ऐसा इसलिए है क्योंकि (प्रति पीईपी 3134) पायथन 3 KeyErrorदूसरे अपवाद के "संदर्भ" ( ) के रूप में पहले अपवाद ( ) को ट्रैक करता है AttributeError, और यदि यह पहुंचता है शीर्ष स्तर, यह एक ट्रेसबैक प्रिंट करेगा जिसमें दोनों अपवाद शामिल हैं। यह तब मददगार हो सकता है जब दूसरा अपवाद अपेक्षित नहीं था, लेकिन यदि आप जानबूझकर दूसरा अपवाद बढ़ा रहे हैं, तो यह अवांछनीय है। पायथन 3.3 के लिए, PEP 415 ने उपयोग करके संदर्भ को दबाने की क्षमता जोड़ी raise AttributeError("whatever") from None
ब्लैंकनथ

3
@ ब्लेकन्थ: एक ट्रेसबैक को प्रिंट करना, जिसमें इस मामले में दोनों अपवाद शामिल हैं, ठीक रहेगा। दूसरे शब्दों में, मुझे नहीं लगता कि आपका कंबल कथन यह है कि यह हमेशा अवांछनीय है। यहां उपयोग में, यह एक KeyErrorमें बदल AttributeErrorरहा है और यह दिखा रहा है कि एक ट्रेसबैक में क्या हुआ उपयोगी और उपयुक्त होगा।
मार्टीन्यू

अधिक जटिल स्थितियों के लिए आप सही हो सकते हैं, लेकिन मुझे लगता है कि जब आप अपवाद प्रकारों के बीच रूपांतरण कर रहे होते हैं, तो आप अक्सर जानते हैं कि पहले अपवाद का विवरण बाहरी उपयोगकर्ता के लिए कोई मायने नहीं रखता है। यही है, अगर __getattr__एक अपवाद उठाता है तो बग शायद विशेषता एक्सेस में एक टाइपो है, न कि वर्तमान वर्ग के कोड में कार्यान्वयन बग। संदर्भ के रूप में पहले के अपवाद को दिखाते हुए उसे कुतर सकते हैं। और यहां तक ​​कि जब आप संदर्भ को दबाते हैं, तब भी आप raise Whatever from Noneपिछले अपवाद पर प्राप्त कर सकते हैं यदि आवश्यक हो ex.__context__
ब्लेकनाथ

1
मैं आपके उत्तर को स्वीकार करना चाहता था लेकिन प्रश्न में मैं अधिक उत्सुक था कि नेस्टेड कोशिश / कैच ब्लॉक का उपयोग करना अच्छा अभ्यास है या नहीं। दूसरी ओर यह सबसे सुंदर समाधान है और मैं इसे अपने कोड में उपयोग करने जा रहा हूं। बहुत धन्यवाद मार्टिन।
मीकल

मिशाल: आपका स्वागत है। यह उपयोग करने से भी तेज है __getattribute__()
मार्टीन्यू

4

पायथन में अनुमति की तुलना में माफी मांगना अधिक आसान है। नेस्टेड अपवाद हैंडलिंग को पसीना न करें।

(इसके अलावा, has*लगभग हमेशा कवर के तहत अपवादों का उपयोग करता है।)


4

प्रलेखन के अनुसार , कई अपवादों को टुपल्स या इस तरह से हैंडल करना बेहतर है:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

2
यह उत्तर वास्तव में मूल प्रश्न को संबोधित नहीं करता है, लेकिन इसे पढ़ने वाले किसी भी व्यक्ति के लिए "नंगे" पर ध्यान दें सिवाय अंत में एक भयानक विचार (आमतौर पर) है क्योंकि यह सब कुछ पकड़ लेगा, जैसे NameError और KeyboardInterrupt - जो आमतौर पर आपका मतलब नहीं है!
खो

यह देखते हुए कि प्रिंट स्टेटमेंट के बाद कोड समान अपवाद को फिर से उठाता है, क्या यह वास्तव में एक बड़ी बात है। इस मामले में यह इसे छिपाए बिना अपवाद पर अधिक संदर्भ प्रदान कर सकता है। अगर यह फिर से नहीं बढ़ा, तो मैं पूरी तरह से सहमत हो जाऊंगा, लेकिन मुझे नहीं लगता कि एक अपवाद को छिपाने का कोई जोखिम है जो आपने इरादा नहीं किया था।
निम्बसस्केल

4

नेस्टेड कोशिश के लिए एक अच्छा और सरल उदाहरण / को छोड़कर निम्नलिखित हो सकता है:

import numpy as np

def divide(x, y):
    try:
        out = x/y
    except:
        try:
            out = np.inf * x / abs(x)
        except:
            out = np.nan
    finally:
        return out

अब विभिन्न संयोजनों का प्रयास करें और आपको सही परिणाम मिलेगा:

divide(15, 3)
# 5.0

divide(15, 0)
# inf

divide(-15, 0)
# -inf

divide(0, 0)
# nan

[निश्चित रूप से हमारे पास सुन्नता है इसलिए हमें इस फ़ंक्शन को बनाने की आवश्यकता नहीं है]


2

एक चीज जिसे मैं टालना पसंद करता हूं वह है एक पुराने को संभालते हुए एक नया अपवाद उठाना। यह त्रुटि संदेशों को पढ़ने के लिए भ्रमित करता है।

उदाहरण के लिए, मेरे कोड में, मैंने मूल रूप से लिखा था

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    raise KeyError(key)

और मुझे यह संदेश मिला।

>>> During handling of above exception, another exception occurred.

मैं यही चाहता था:

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    pass
raise KeyError(key)

यह प्रभावित नहीं करता है कि अपवादों को कैसे नियंत्रित किया जाता है। कोड के किसी भी ब्लॉक में, एक KeyError को पकड़ा गया होगा। यह केवल शैली अंक प्राप्त करने का मुद्दा है।


from Noneहालांकि, और भी अधिक शैली बिंदुओं के लिए, उठाए गए उत्तर के उपयोग को देखें । :)
Pianosaurus

1

यदि प्रयास-अपवाद-अंत में अंत में ब्लॉक के अंदर नेस्टेड है, तो "बच्चे" से परिणाम अंततः संरक्षित है। मुझे अभी तक आधिकारिक विस्तार नहीं मिला है, लेकिन निम्नलिखित कोड स्निपेट पायथन 3.6 में इस व्यवहार को दर्शाता है।

def f2():
    try:
        a = 4
        raise SyntaxError
    except SyntaxError as se:
        print('log SE')
        raise se from None
    finally:
        try:
            raise ValueError
        except ValueError as ve:
            a = 5
            print('log VE')
            raise ve from None
        finally:
            return 6       
        return a

In [1]: f2()
log SE
log VE
Out[2]: 6

यह व्यवहार @ स्लावोमिर लेनार्ट द्वारा दिए गए उदाहरण से अलग है जब अंत में ब्लॉक को छोड़कर अंदर घोंसला होता है।
गोंगहुआ शू

0

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


डॉक्स से: एक बहु-थ्रेडेड वातावरण में, LBYL (लुक बिफोर यू लीप) दृष्टिकोण " लुकिंग " और "लीपिंग" के बीच दौड़ की स्थिति को पेश करने का जोखिम उठा सकता है। उदाहरण के लिए, कोड, यदि मैपिंग में कुंजी: रिटर्न मैपिंग [कुंजी] विफल हो सकता है यदि परीक्षण के बाद एक और धागा मैपिंग से कुंजी को हटाता है, लेकिन देखने से पहले। इस मुद्दे को ताले के साथ या ईएएफपी (अनुमति की तुलना में माफी मांगने में आसान) का उपयोग करके हल किया जा सकता है
नूनो एंड्रे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.