কখন এবং কীভাবে আমার ব্যতিক্রম ব্যবহার করা উচিত?


20

সেটিং

কখন এবং কীভাবে ব্যতিক্রম ব্যবহার করতে হয় তা নির্ধারণ করতে আমার প্রায়শই সমস্যা হয়। আসুন একটি সাধারণ উদাহরণ বিবেচনা করুন: মনে করুন যে আমি একটি ওয়েবপৃষ্ঠা স্ক্র্যাপ করছি , আবে ভিগোদা এখনও বেঁচে আছে কিনা তা নির্ধারণ করতে " http://www.abevigoda.com/ " বলুন । এটি করার জন্য, আমাদের কেবল পৃষ্ঠাটি ডাউনলোড করতে হবে এবং "আবে ভিগোদা" শব্দটি প্রদর্শিত হবে এমন সময়গুলির সন্ধান করুন। আমরা প্রথম উপস্থিতি ফিরে আসি, যেহেতু এর মধ্যে আবের স্থিতি অন্তর্ভুক্ত। ধারণাগতভাবে, এটি দেখতে এটির মতো হবে:

def get_abe_status(url):
    # download the page
    page = download_page(url)

    # get all mentions of Abe Vigoda
    hits = page.find_all_mentions("Abe Vigoda")

    # parse the first hit for his status
    status = parse_abe_status(hits[0])

    # he's either alive or dead
    return status == "alive"

যেখানে parse_abe_status(s)"আবে ভিগোদা কিছু হয়" ফর্মটির একটি স্ট্রিং নিয়ে যায় এবং " কিছু " অংশ প্রদান করে।

আপনার যুক্তির আগে এই যে আবের স্থিতির জন্য এই পৃষ্ঠাটি স্ক্র্যাপ করার আরও ভাল এবং আরও শক্তিশালী উপায় রয়েছে, মনে রাখবেন যে এটি আমি যে একটি সাধারণ পরিস্থিতি হাইলাইট করার জন্য ব্যবহৃত একটি সাধারণ এবং নিয়মিত উদাহরণ।

এখন, এই কোডটি কোথায় সমস্যার সম্মুখীন হতে পারে? অন্যান্য ত্রুটিগুলির মধ্যে কিছু "প্রত্যাশিত" টি হ'ল:

  • download_pageপৃষ্ঠাটি ডাউনলোড করতে সক্ষম না হতে পারে এবং একটি নিক্ষেপ করে IOError
  • URL টি সঠিক পৃষ্ঠায় নির্দেশ নাও করতে পারে, বা পৃষ্ঠাটি ভুলভাবে ডাউনলোড করা হয়েছে, এবং তাই কোনও হিট নেই। hitsখালি তালিকা, তারপর।
  • ওয়েব পৃষ্ঠাটি পরিবর্তন করা হয়েছে, সম্ভবত পৃষ্ঠাটি সম্পর্কে আমাদের অনুমানগুলি ভুল করেছে। সম্ভবত আমরা অ্যাবে ভিগোদার 4 টি উল্লেখ আশা করি, তবে এখন আমরা 5 টি পেয়েছি।
  • কিছু কারণে, hits[0]"অ্যাবে ভিগোডা কিছু " এই ফর্মের স্ট্রিং নাও থাকতে পারে এবং তাই এটি সঠিকভাবে বিশ্লেষণ করা যায় না।

প্রথম IOErrorকেসটি আমার পক্ষে আসলেই সমস্যা নয়: একটি নিক্ষিপ্ত হয় এবং আমার ফাংশনটির কলকারী এটি পরিচালনা করতে পারে। সুতরাং আসুন অন্যান্য কেসগুলি এবং আমি কীভাবে তাদের পরিচালনা করতে পারি তা বিবেচনা করা যাক। তবে প্রথমে, ধরে নেওয়া যাক আমরা parse_abe_statusসম্ভব বোকামি উপায়ে বাস্তবায়ন করি :

def parse_abe_status(s):
    return s[13:]

যথা, এটি কোনও ত্রুটি পরীক্ষা করে না। এখন, বিকল্পগুলিতে:

বিকল্প 1: ফিরে None

আমি কলকারীকে বলতে পারি যে ফিরে এসে কিছু ভুল হয়েছে None:

def get_abe_status(url):
    # download the page
    page = download_page(url)

    # get all mentions of Abe Vigoda
    hits = page.find_all_mentions("Abe Vigoda")

    if not hits:
        return None

    # parse the first hit for his status
    status = parse_abe_status(hits[0])

    # he's either alive or dead
    return status == "alive"

যদি কলকারী Noneআমার ফাংশনটি থেকে গ্রহণ করে তবে তার ধরে নেওয়া উচিত যে আবে ভিগোদার কোনও উল্লেখ নেই, এবং তাই কিছু ভুল হয়েছে। তবে এটি বেশ অস্পষ্ট, তাই না? এবং এটি কেসটিকে সহায়তা করে না যেখানে hits[0]আমরা যা ভেবেছিলাম তা নয়।

অন্যদিকে, আমরা কিছু ব্যতিক্রম রাখতে পারি:

বিকল্প 2: ব্যতিক্রম ব্যবহার করে

যদি hitsখালি থাকে, একটি IndexErrorনিক্ষিপ্ত হবে যখন আমরা প্রচেষ্টা hits[0]। তবে কলকারী IndexErrorআমার ফাংশনটি নিক্ষেপ করবেন বলে আশা করা উচিত নয় , যেহেতু তিনি জানেন না যে এটি IndexErrorকোথা থেকে এসেছে; এটি ছুঁড়ে ফেলা যেত find_all_mentions, কারণ তিনি জানেন। সুতরাং আমরা এটি পরিচালনা করতে একটি কাস্টম ব্যতিক্রম ক্লাস তৈরি করব:

class NotFoundError(Exception):
    """Throw this when something can't be found on a page."""

def get_abe_status(url):
    # download the page
    page = download_page(url)

    # get all mentions of Abe Vigoda
    hits = page.find_all_mentions("Abe Vigoda")

    try:
        hits[0]
    except IndexError:
        raise NotFoundError("No mentions found.")

    # parse the first hit for his status
    status = parse_abe_status(hits[0])

    # he's either alive or dead
    return status == "alive"

এখন যদি পৃষ্ঠাটি পরিবর্তন হয়ে যায় এবং হিটের অপ্রত্যাশিত সংখ্যক সংখ্যা থাকে? কোডটি এখনও কাজ করতে পারে বলে এটি বিপর্যয়কর নয়, তবে একজন কলকারী অতিরিক্ত সতর্কতা অবলম্বন করতে চান, বা তিনি কোনও সতর্কতা লগ করতে চাইতে পারেন। সুতরাং আমি একটি সতর্কতা নিক্ষেপ করব:

class NotFoundError(Exception):
    """Throw this when something can't be found on a page."""

def get_abe_status(url):
    # download the page
    page = download_page(url)

    # get all mentions of Abe Vigoda
    hits = page.find_all_mentions("Abe Vigoda")

    try:
        hits[0]
    except IndexError:
        raise NotFoundError("No mentions found.")

    # say we expect four hits...
    if len(hits) != 4:
        raise Warning("An unexpected number of hits.")
        logger.warning("An unexpected number of hits.")

    # parse the first hit for his status
    status = parse_abe_status(hits[0])

    # he's either alive or dead
    return status == "alive"

শেষ অবধি, আমরা দেখতে পাব যে statusতা জীবিত বা মৃত নয়। হতে পারে, কোনও অদ্ভুত কারণে, আজ এটি পরিণত হয়েছিল comatose। তারপরে আমি আর ফিরে আসতে চাই না False, কারণ এতে বোঝা যায় যে আবে মারা গেছেন। আমার এখানে কি করা উচিত? সম্ভবত একটি ব্যতিক্রম নিক্ষেপ। তবে কী রকম? আমি কি কাস্টম ব্যতিক্রম ক্লাস তৈরি করব?

class NotFoundError(Exception):
    """Throw this when something can't be found on a page."""

def get_abe_status(url):
    # download the page
    page = download_page(url)

    # get all mentions of Abe Vigoda
    hits = page.find_all_mentions("Abe Vigoda")

    try:
        hits[0]
    except IndexError:
        raise NotFoundError("No mentions found.")

    # say we expect four hits...
    if len(hits) != 4:
        raise Warning("An unexpected number of hits.")
        logger.warning("An unexpected number of hits.")

    # parse the first hit for his status
    status = parse_abe_status(hits[0])

    if status not in ['alive', 'dead']:
        raise SomeTypeOfError("Status is an unexpected value.")

    # he's either alive or dead
    return status == "alive"

বিকল্প 3: কোথাও কোথাও

আমি মনে করি যে ব্যাতিক্রম সহ দ্বিতীয় পদ্ধতিটি পছন্দনীয়, তবে আমি নিশ্চিত নই যে আমি এর মধ্যে ব্যতিক্রমগুলি সঠিকভাবে ব্যবহার করছি কিনা। আরও অভিজ্ঞ প্রোগ্রামাররা কীভাবে এটি পরিচালনা করবে তা দেখার জন্য আমি আগ্রহী।

উত্তর:


17

পাইথনের সুপারিশটি ব্যর্থতা নির্দেশ করতে ব্যতিক্রমগুলি ব্যবহার করা। আপনি নিয়মিত ব্যর্থতা আশা করলেও এটি সত্য।

আপনার কোডের কলারের দৃষ্টিকোণ থেকে এটি দেখুন:

my_status = get_abe_status(my_url)

আমরা যদি কিছুই না ফেরাই? যদি কলার বিশেষভাবে কেস_অব_স্যাটাস ব্যর্থ হওয়া কেসটি পরিচালনা না করে তবে এটি কেবল_আপনি কোনও না থাকায় চালিয়ে যাওয়ার চেষ্টা করবে। এটি পরবর্তীতে বাগ নির্ণয় করতে অসুবিধা সৃষ্টি করতে পারে। এমনকি যদি আপনি কারও জন্য চেক করেন না কেন, get_abe_status () ব্যর্থ হয়েছে কেন এই কোডটির কোনও ক্লু নেই।

কিন্তু যদি আমরা একটি ব্যতিক্রম উত্থাপন? যদি কলার বিশেষভাবে কেসটি পরিচালনা করে না, ব্যতিক্রমটি অবশেষে ডিফল্ট ব্যতিক্রম হ্যান্ডলারটিকে আঘাত করে উপরের দিকে প্রচার করবে propag এটি আপনি যা চান তা নাও হতে পারে তবে প্রোগ্রামের অন্য কোথাও একটি সূক্ষ্ম বাগ প্রবর্তন করা তার চেয়ে ভাল। অতিরিক্ত হিসাবে, ব্যতিক্রমটি কী ভুল হয়েছে যা প্রথম সংস্করণে হারিয়ে গেছে সে সম্পর্কে তথ্য দেয়।

কলারের দৃষ্টিকোণ থেকে, এটির ফিরে আসার চেয়ে একটি ব্যতিক্রম পাওয়ার জন্য আরও সহজ more ব্যর্থতার শর্তগুলি মান না ফেরানোর জন্য ব্যতিক্রমগুলি ব্যবহার করার জন্য এটি অজগর স্টাইল।

কেউ কেউ আলাদা দৃষ্টিকোণ নেবে এবং তর্ক করবে যে আপনি কেবল সেই ক্ষেত্রে ব্যতিক্রম ব্যবহার করতে পারেন যা আপনি কখনই ঘটবেন বলে আশা করেন না। তারা যুক্তি দেয় যে সাধারণত দৌড়াদৌড়ি দৌড়ানো কোনও ব্যতিক্রম বাড়াতে পারে না। এর জন্য দেওয়া একটি কারণ হ'ল ব্যতিক্রমগুলি চূড়ান্তভাবে অক্ষম, তবে পাইথনের পক্ষে এটি সত্য নয়।

আপনার কোডে কয়েকটি পয়েন্ট:

try:
    hits[0]
except IndexError:
    raise NotFoundError("No mentions found.")

খালি তালিকার জন্য এটি পরীক্ষা করা সত্যিই বিভ্রান্তিমূলক উপায়। কিছু পরীক্ষা করার জন্য ব্যতিক্রম প্ররোচিত করবেন না। একটি ব্যবহার করুন।

# say we expect four hits...
if len(hits) != 4:
    raise Warning("An unexpected number of hits.")
    logger.warning("An unexpected number of hits.")

আপনি বুঝতে পেরেছেন যে লগার.ওয়ার্নিং লাইনটি কখনই ঠিক চলবে না?


1
আপনার প্রতিক্রিয়ার জন্য ধন্যবাদ (বিস্মৃতভাবে) এটি প্রকাশিত কোড দেখার সাথে সাথে কখন এবং কীভাবে একটি ব্যতিক্রম ছুঁড়ে ফেলা যায় সে সম্পর্কে আমার অনুভূতি উন্নত করেছে।
jme

4

গৃহীত উত্তরটি গ্রহণযোগ্যতার দাবিদার এবং প্রশ্নের উত্তর দেয়, আমি এটি কেবল কিছুটা অতিরিক্ত ব্যাকগ্রাউন্ড সরবরাহ করতে লিখি।

পাইথনের একটি ক্রেডিও হ'ল: অনুমতি চেয়ে ক্ষমা চাওয়া সহজ। এর অর্থ হ'ল সাধারণত আপনি কেবল জিনিসগুলি করেন এবং যদি আপনি ব্যতিক্রমগুলি আশা করেন তবে আপনি সেগুলি পরিচালনা করেন। আপনি যদি কোনও ব্যতিক্রম পাবেন না তা নিশ্চিত করার জন্য হাতের আগে চেক করে নিলে তার বিপরীতে।

সি ++ / জাভা থেকে মানসিকতার মধ্যে পার্থক্য কতটা নাটকীয়, তা দেখানোর জন্য আমি একটি উদাহরণ দিতে চাই। সি ++ এ লুপের জন্য সাধারণত এমন কিছু দেখাচ্ছে:

for(int i = 0; i != myvector.size(); ++i) ...

এটি সম্পর্কে চিন্তা করার একটি উপায়: myvector[k]k> = মাইভেেক্টর.সাইজ () যেখানে অ্যাক্সেস করা তার ব্যতিক্রম ঘটায়। সুতরাং আপনি চেষ্টা করে নীতিগতভাবে এটি (খুব বিশ্রীভাবে) লিখতে পারেন।

    for(int i = 0; ; ++i)  {
        try {
           ...
        } catch (& std::out_of_range)
             break

বা অনুরূপ কিছু। এখন, লুপের জন্য অজগরটিতে কী ঘটছে তা বিবেচনা করুন:

for i in range(1):
    ...

এটি কীভাবে কাজ করছে? ফর লুপটি ব্যাপ্তি (1) এর ফলাফল গ্রহণ করে এবং এটিতে ইটার () কল করে এটিতে একটি পুনরুক্তিকারীকে ধরে ফেলবে।

b = range(1).__iter__()

তারপরে এটি প্রতিটি লুপের পুনরাবৃত্তির উপরে এটি কল করে, যতক্ষণ না ...:

>>> next(b)
0
>>> next(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

অন্য কথায়, অজগরের জন্য একটি লুপ আসলে ছদ্মবেশে ব্যতীত চেষ্টা করা।

যতক্ষণ না কংক্রিট প্রশ্নটি চলে যায়, মনে রাখবেন যে ব্যতিক্রমগুলি স্বাভাবিক ফাংশন সম্পাদন বন্ধ করে দেয় এবং পৃথকভাবে মোকাবেলা করতে হবে। পাইথনে, যখনই আপনার ফাংশনে বাকী কোডটি কার্যকর করার কোনও বিন্দু না আসে এবং আপনি / বা কোনও কিছুই ফাংশনে কী ঘটেছিল তা সঠিকভাবে প্রতিফলিত করে না এমন সময় আপনার অবাধে সেগুলি ছুঁড়ে ফেলা উচিত। নোট করুন যে কোনও ফাংশন থেকে তাড়াতাড়ি ফিরতে আলাদা: তাড়াতাড়ি ফিরে আসার অর্থ আপনি ইতিমধ্যে উত্তরটি খুঁজে পেয়েছেন এবং উত্তরটি বের করার জন্য বাকী কোডটির দরকার নেই। আমি বলছি যে উত্তরটি জানা না গেলে ব্যতিক্রম ছুঁড়ে ফেলা উচিত এবং উত্তর নির্ধারণের জন্য কোডের বাকী অংশটি যুক্তিসঙ্গতভাবে চালানো যাবে না। এখন, নিজেকে "সঠিকভাবে প্রতিফলিত করুন", আপনি যে ব্যতিক্রমগুলি ছুঁড়ে ফেলতে পছন্দ করেন তা হ'ল ডকুমেন্টেশনের বিষয়।

আপনার নির্দিষ্ট কোডের ক্ষেত্রে, আমি বলব যে কোনও পরিস্থিতি যার ফলে হিটগুলি একটি খালি তালিকা হতে পারে তা নিক্ষেপ করা উচিত। কেন? ঠিক আছে, আপনার ফাংশনটি যেভাবে সেট আপ হয়েছে, হিটকে পার্সিং না করে উত্তর নির্ধারণের কোনও উপায় নেই। সুতরাং যদি হিটগুলি পার্সেবল না হয়, হয় URL টি খারাপ, বা হিট খালি থাকার কারণে, তাহলে ফাংশনটি প্রশ্নের উত্তর দিতে পারে না, এবং বাস্তবে এমনকি চেষ্টা করার চেষ্টাও করতে পারে না।

এই বিশেষ ক্ষেত্রে, আমি তর্ক করব যে আপনি পার্স করার ব্যবস্থাও করেন এবং যুক্তিসঙ্গত উত্তর না পেলে (জীবিত বা মৃত), তারপরেও আপনার ফেলে দেওয়া উচিত। কেন? কারণ, ফাংশনটি একটি বুলিয়ান দেয়। কোনওটিই ফিরিয়ে দেওয়া আপনার ক্লায়েন্টের পক্ষে খুব বিপজ্জনক নয়। যদি তারা কেউ না-ও-তে পরীক্ষা করে রাখে তবে ব্যর্থতা হবে না, এটি নিঃশব্দে মিথ্যা হিসাবে গণ্য হবে। সুতরাং, আপনার ক্লায়েন্টকে মূলত সর্বদা এটি করা উচিত যদি কোনও হয় না তবে তিনি চুপ করে ব্যর্থতা চান না কিনা তা চেক করুন ... সুতরাং আপনার সম্ভবত সম্ভবত ছুঁড়ে দেওয়া উচিত।


2

যখন ব্যতিক্রমী কিছু ঘটে তখন আপনার ব্যতিক্রম ব্যবহার করা উচিত । এটি হ'ল অ্যাপ্লিকেশনটির যথাযথ ব্যবহারের ফলে এমন কিছু ঘটতে পারে না। এটি যদি খুঁজে পাওয়া যায় না এমন কোনও কিছু অনুসন্ধানের জন্য যদি আপনার পদ্ধতির ভোক্তার পক্ষে অনুমোদিত এবং প্রত্যাশিত হয় তবে "খুঁজে পাওয়া যায় না" এটি ব্যতিক্রমী মামলা নয়। এই ক্ষেত্রে, আপনার নাল বা "কিছুই নয়" বা {}, বা কোনও খালি রিটার্ন সেটটি নির্দেশ করে এমন কিছু ফিরে আসা উচিত।

অন্যদিকে, আপনি যদি সত্যিই আপনার পদ্ধতির গ্রাহকদের কাছে সর্বদা প্রত্যাশা রাখেন (যদি না তারা কোনওরকম ভুল করে) তবে যা অনুসন্ধান করা হচ্ছে তা খুঁজে বের করা, তবে এটি সন্ধান না করা একটি ব্যতিক্রম হবে এবং আপনার সাথে এটি করা উচিত।

মূলটি হ'ল ব্যতিক্রম হ্যান্ডলিং ব্যয়বহুল হতে পারে - ব্যতিক্রমগুলি ভাবা হয় যেগুলি যখন ঘটেছিল তখন আপনার অ্যাপ্লিকেশনটির অবস্থা সম্পর্কে তথ্য সংগ্রহ করে, যেমন স্ট্যাক ট্রেস, ভাবাপন্নদের কেন ঘটেছিল তা বুঝতে সাহায্য করার জন্য। আপনি যা করার চেষ্টা করছেন তা আমি মনে করি না।


1
আপনি যদি সিদ্ধান্ত নেন যে কোনও মান খুঁজে পাওয়া অনুমোদিত নয়, যা ঘটেছিল তা বোঝাতে আপনি কী ব্যবহার করেন সে সম্পর্কে সতর্ক থাকুন। যদি আপনার পদ্ধতির কোনওটি ফেরত আসার কথা Stringএবং আপনি যদি আপনার সূচক হিসাবে "কিছুই না" বেছে নেন তবে এর অর্থ হ'ল আপনাকে "সতর্কতা অবলম্বন করতে হবে যে" কোনওটিই কখনই কোনও বৈধ মান হবে না। এছাড়াও নোট করুন যে ডেটা দেখার জন্য এবং কোনও মান খুঁজে না পাওয়া এবং ডেটা পুনরুদ্ধার করতে না পারার মধ্যে পার্থক্য রয়েছে, সুতরাং আমরা তথ্যটি খুঁজে পাই না। এই দুটি ক্ষেত্রে একই ফলাফল হওয়ার অর্থ আপনি যখন কোনও মান পাচ্ছেন একবার আপনি যখন কোনও মান পাচ্ছেন না তখন আপনার কোনও দৃশ্যমানতা নেই।
unholysampler

ইনলাইন কোড ব্লকগুলি ব্যাকটিক্স (`) দিয়ে চিহ্নিত করা হয়েছে, সম্ভবত আপনি" কিছুই নেই "এর সাথে বোঝাতে চেয়েছিলেন?
ইজকাটা

3
আমি আশঙ্কা করি পাইথনে এটি সম্পূর্ণ মিথ্যা। আপনি অন্য ভাষায় সি ++ / জাভা শৈলী যুক্তি প্রয়োগ করছেন। পাইথন লুপের জন্য প্রান্তটি নির্দেশ করতে ব্যতিক্রমগুলি ব্যবহার করে; এটা বেশ অবাস্তব।
নীড় ফ্রাইডম্যান

2

আমি যদি একটি ফাংশন লিখছিলাম

 def abe_is_alive():

আমি এটি লিখতে return Trueবা Falseযে ক্ষেত্রে আমি একে অপর সম্পর্কে একেবারে নিশ্চিত এবং অন্য raiseকোনও ক্ষেত্রে ত্রুটি (যেমন raise ValueError("Status neither 'dead' nor 'alive'"))। এটি কারণ ফাংশন কলিংটি একটি বুলিয়ান আশা করে, এবং যদি আমি নিশ্চিতভাবে এটি সরবরাহ করতে না পারি তবে নিয়মিত প্রোগ্রামের প্রবাহটি চালিয়ে যাওয়া উচিত নয়।

প্রত্যাশার চেয়ে আলাদা সংখ্যক "হিট" পাওয়ার উদাহরণের মতো কিছু আমি সম্ভবত উপেক্ষা করব; যতক্ষণ হিটগুলির একটি এখনও আমার প্যাটার্নের সাথে মেলে "আবে ভিগোদা {মৃত | জীবিত}", ঠিক আছে। এটি পৃষ্ঠাটিকে পুনরায় সাজানোর অনুমতি দেয় তবে এখনও উপযুক্ত তথ্য পায়।

বরং

try:
    hits[0] 
except IndexError:
    raise NotFoundError

আমি স্পষ্টভাবে পরীক্ষা করতে হবে:

if not hits:
    raise NotFoundError

যেহেতু এটি "সস্তা" হতে থাকে তারপরে সেট আপ করুন try

আমি আপনার সাথে একমত IOError; ওয়েবসাইটের সাথে সংযোগ করার ক্ষেত্রে আমিও ত্রুটি করার চেষ্টা করব না - যদি আমরা না পারি তবে কোনও কারণে এটি হ্যান্ডেল করার উপযুক্ত স্থান নয় (কারণ এটি আমাদের প্রশ্নের উত্তর দিতে আমাদের সহায়তা করে না) এবং এটি পাস করা উচিত কলিং ফাংশন আউট।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.