পাইথনের প্রবাহ নিয়ন্ত্রণের জন্য ব্যতিক্রমগুলি কি সেরা?


15

আমি "পাইথন শিখছি" পড়ছি এবং নিম্নলিখিতগুলি জুড়ে এসেছি:

ব্যবহারকারী-সংজ্ঞায়িত ব্যতিক্রমগুলি ননররর অবস্থার সংকেতও দিতে পারে। উদাহরণস্বরূপ, একজন অনুসন্ধানের রুটিনকে ব্যতিক্রম বাড়াতে কোডিং করা যেতে পারে যখন কোনও কলকারীর ব্যাখ্যার জন্য স্থিতির পতাকা ফেরানোর পরিবর্তে কোনও ম্যাচ পাওয়া যায়। নিম্নলিখিতটিতে, চেষ্টা করুন / বাদে / অন্য ব্যতিক্রম হ্যান্ডলারটি যদি / অন্যটি রিটার্ন-মান পরীক্ষকের কাজ করে:

class Found(Exception): pass

def searcher():
    if ...success...:
        raise Found()            # Raise exceptions instead of returning flags
    else:
        return

পাইথনটি গতিশীলভাবে টাইপযুক্ত এবং মূলটিতে বহুবিধ, তাই ব্যতিক্রমগুলি সেন্ডিনেল রিটার্ন মানগুলির পরিবর্তে এই জাতীয় অবস্থার সংকেত দেওয়ার সাধারণ পছন্দ way

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

এটি ( নিয়ন্ত্রণ প্রবাহ হিসাবে ব্যতিক্রমগুলি কি মারাত্মক অ্যান্টিপ্যাটার্ন হিসাবে বিবেচিত হয়? যদি তাই হয় তবে কেন? ) এছাড়াও বেশ কয়েকটি মন্তব্যকারী লিখেছেন যে এই স্টাইলটি পাইথোনিক। এটি কিসের উপর ভিত্তি করে?

টিয়া

উত্তর:


25

সাধারণ sensকমত্য "ব্যতিক্রম ব্যবহার করবেন না!" বেশিরভাগই অন্যান্য ভাষা থেকে আসে এবং এমনকি কখনও কখনও পুরানো হয়।

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

  • সি ++ গোড়ার দিকে, প্রচুর কোড ব্যতিক্রম-নিরাপদ পদ্ধতিতে লেখা হয়নি: আপনি যদি সত্যই RAII ব্যবহার না করেন তবে মেমরি এবং অন্যান্য সংস্থানগুলি ফাঁস করা সহজ। সুতরাং ব্যতিক্রম ছোঁড়া সেই কোডটি ভুল উপস্থাপন করবে। এটি আর যুক্তিসঙ্গত নয় যেহেতু এমনকি সি ++ স্ট্যান্ডার্ড লাইব্রেরিও ব্যতিক্রমগুলি ব্যবহার করে: আপনি ব্যতিক্রমের অস্তিত্বের উপস্থিতি করতে পারবেন না। যাইহোক, সি কোডটি সি ++ এর সাথে সংযুক্ত করার সময় ব্যতিক্রমগুলি এখনও একটি বিষয়।

  • জাভাতে, প্রতিটি ব্যতিক্রমের একটি সম্পর্কিত স্ট্যাক ট্রেস রয়েছে। ত্রুটিগুলি ডিবাগ করার সময় স্ট্যাক ট্রেসটি অত্যন্ত মূল্যবান, তবে ব্যতিক্রম কখনই প্রিন্ট করা হয় না, যেমন: এটি কেবল নিয়ন্ত্রণ প্রবাহের জন্যই ব্যবহৃত হত।

সুতরাং সেই ভাষাগুলিতে ব্যতিক্রমগুলি "খুব ব্যয়বহুল" নিয়ন্ত্রণ প্রবাহ হিসাবে ব্যবহার করা যায় না। পাইথনে এটি কোনও ইস্যু কম এবং ব্যতিক্রমগুলি অনেক সস্তা। অতিরিক্তভাবে, পাইথন ভাষা ইতিমধ্যে কিছু ওভারহেডের সাথে ভুগেছে যা অন্যান্য নিয়ন্ত্রণ প্রবাহের কনস্ট্রাক্টগুলির তুলনায় ব্যতিক্রমগুলির ব্যয়কে অযৌক্তিক করে তোলে: উদাহরণস্বরূপ, কোনও স্পষ্ট সদস্যপদ পরীক্ষার সাথে ডিক এন্ট্রি উপস্থিত রয়েছে কিনা তা পরীক্ষা করে if key in the_dict: ...নিছক প্রবেশদ্বারটি অ্যাক্সেস করার চেয়ে ঠিক তত দ্রুত the_dict[key]; ...এবং আপনি পরীক্ষা করছেন কিনা একটি কীআরআর পান। কিছু অবিচ্ছেদ্য ভাষা বৈশিষ্ট্য (যেমন জেনারেটর) ব্যতিক্রমগুলির ক্ষেত্রে ডিজাইন করা হয়েছে।

সুতরাং পাইথনের ব্যতিক্রমগুলি এড়াতে কোনও প্রযুক্তিগত কারণ না থাকলেও, এখনও ফেরতের মানগুলির পরিবর্তে আপনার সেগুলি ব্যবহার করা উচিত কিনা তা নিয়ে এখনও প্রশ্ন রয়েছে। ব্যতিক্রমগুলি সহ ডিজাইন-স্তরের সমস্যাগুলি হ'ল:

  • এগুলি মোটেই সুস্পষ্ট নয়। আপনি সহজেই কোনও ফাংশনটি দেখতে পারবেন না এবং এটি দেখতে পাবে যে কোনও ব্যতিক্রমগুলি এটি ফেলে দিতে পারে, তাই আপনি কী ধরবেন তা আপনি সর্বদা জানেন না। রিটার্ন মানটি আরও সু-সংজ্ঞায়িত হতে থাকে।

  • ব্যতিক্রমগুলি অ-স্থানীয় নিয়ন্ত্রণ প্রবাহ যা আপনার কোডকে জটিল করে তোলে। আপনি যখন ব্যতিক্রম ছুঁড়ে মারেন, আপনি জানেন না যে নিয়ন্ত্রণ প্রবাহটি আবার শুরু হবে। ত্রুটিগুলির জন্য যা অবিলম্বে পরিচালনা করা যায় না এটি সম্ভবত একটি ভাল ধারণা, যখন আপনার কলারের শর্তটি অবহিত করার সময় এটি সম্পূর্ণ অপ্রয়োজনীয়।

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

try:
  list_contains(invited_guests, person_at_door)
except Found:
  print("Oh, hello {}!".format(person_at_door))
except NotFound:
  print("Who are you?")

একটি বিল ফিরিয়ে দেওয়া আরও পরিষ্কার হবে:

if list_contains(invited_guests, person_at_door):
  print("Oh, hello {}!".format(person_at_door))
else:
  print("Who are you?")

যদি ফাংশনটি ইতিমধ্যে কোনও মান ফেরত দেওয়ার কথা মনে করা হয়, তবে বিশেষ অবস্থার জন্য একটি বিশেষ মান ফিরিয়ে দেওয়া বরং ত্রুটি-ঝুঁকির কারণ, লোকেরা এই মানটি পরীক্ষা করতে ভুলে যাবে (এটি সম্ভবত সি এর মধ্যে 1/3 সমস্যার কারণ)। একটি ব্যতিক্রম সাধারণত আরও সঠিক।

একটি ভাল উদাহরণ একটি pos = find_string(haystack, needle)ফাংশন যা needle`খড়ের কাঠের স্ট্রিংয়ের স্ট্রিংয়ের প্রথম উপস্থিতি সন্ধান করে এবং প্রারম্ভিক অবস্থানে ফিরে আসে। তবে কী যদি তারা খড় খড়ের স্ট্রিতে সুই-স্ট্রিং না থাকে?

সি দ্বারা সমাধান এবং পাইথন দ্বারা নকল করা হল একটি বিশেষ মান ফেরত। সিতে এটি একটি নাল পয়েন্টার, পাইথনে এটি -1। এটি পজিশন ছাড়াই স্ট্রিং সূচক হিসাবে অবস্থানটি ব্যবহার করা হলে এটি বিস্ময়কর ফলাফলের দিকে নিয়ে যায়, বিশেষত -1পাইথনের বৈধ সূচক হিসাবে । সি-তে, আপনার ন্যূনাল পয়েন্টারটি আপনাকে কমপক্ষে একটি সেগফল্ট দেবে।

পিএইচপি-তে, ভিন্ন ধরণের একটি বিশেষ মান ফিরে আসে: FALSEএকটি পূর্ণসংখ্যার পরিবর্তে বুলিয়ান । যেহেতু এটি দেখা যাচ্ছে এটি ভাষার অন্তর্নিহিত নিয়মের কারণে আসলে এর চেয়ে ভাল কিছু নয় (তবে লক্ষ্য করুন যে পাইথনের পাশাপাশি বুলিয়ানও ইনট হিসাবে ব্যবহার করা যেতে পারে!)। ক্রমাগত ধরণের ফাংশন না দেয় এমন ফাংশনগুলি সাধারণত খুব বিভ্রান্তিকর বলে বিবেচিত হয়।

আরও শক্তিশালী বৈকল্পিকটি একটি ব্যতিক্রম ছুঁড়ে ফেলা হত যখন স্ট্রিংটি পাওয়া যায় না, যা নিশ্চিত করে যে স্বাভাবিক নিয়ন্ত্রণ প্রবাহের সময় দুর্ঘটনাক্রমে একটি সাধারণ মানের জায়গায় বিশেষ মানটি ব্যবহার করা অসম্ভব:

 try:
   pos = find_string(haystack, needle)
   do_something_with(pos)
 except NotFound:
   ...

বিকল্পভাবে, সর্বদা এমন টাইপ ফিরানো যা সরাসরি ব্যবহার করা যায় না তবে প্রথমে আবদ্ধ হওয়া আবশ্যক, উদাহরণস্বরূপ ফলাফল-বুল টুপল যেখানে বুলিয়ান ইঙ্গিত দেয় যে কোনও ব্যতিক্রম ঘটেছে কিনা বা ফলাফল ব্যবহারযোগ্য কিনা। তারপর:

pos, ok = find_string(haystack, needle)
if not ok:
  ...
do_something_with(pos)

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

সুতরাং সংক্ষেপে বলতে গেলে ব্যতিক্রমগুলি পুরোপুরি সমস্যা ছাড়াই নয় এবং যথাযথভাবে অতিরিক্ত ব্যবহার করা যায়, বিশেষত যখন তারা একটি "স্বাভাবিক" রিটার্ন মান প্রতিস্থাপন করে। তবে যখন বিশেষ শর্তগুলির সংকেত দেওয়ার জন্য ব্যবহৃত হয় (অগত্যা কেবল ত্রুটি নয়) তবে ব্যতিক্রমগুলি আপনাকে এমন এপিআই বিকাশ করতে সহায়তা করবে যা পরিষ্কার, স্বজ্ঞাত, ব্যবহারযোগ্য সহজ এবং অপব্যবহার করা কঠিন।


1
গভীর উত্তরের জন্য ধন্যবাদ। আমি জাভা এর মতো অন্যান্য ভাষার পটভূমি থেকে আসছি যেখানে "ব্যতিক্রম কেবল ব্যতিক্রমী শর্তের জন্য," তাই এটি আমার কাছে নতুন। ইএএফপি, ইআইবিটিআই ইত্যাদির মতো পাইথন নির্দেশিকাগুলি (মেইলিং লিস্টে, ব্লগ পোস্টে, ইত্যাদিতে) সাথে কোনও পাইথন কোর ডেভস রয়েছে যা তারা কখনও এভাবে বলেছিল I'd অন্য দেব / বস জিজ্ঞাসা। ধন্যবাদ!
জেবি

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

আপনার পরিচয়ের প্রথম বুলেট প্রথমে বিভ্রান্তিকর ছিল was হতে পারে আপনি এটিকে "আধুনিক সি ++ এ ব্যতিক্রম হ্যান্ডলিং কোডটি শূন্য-ব্যয়বহুল; তবে একটি ব্যতিক্রম নিক্ষেপ ব্যয়বহুল" এর মতো কিছুতে পরিবর্তন করতে পারেন ? (lurkers জন্য: stackoverflow.com/questions/13835817/... ) [সম্পাদনা: সম্পাদনা প্রস্তাবিত]
phresnel

নোট করুন যে অভিধানের অপারেশনগুলির জন্য একটি ব্যবহার করে collections.defaultdictবা my_dict.get(key, default)কোডটি কোডের থেকে আরও স্পষ্ট করে তোলেtry: my_dict[key] except: return default
স্টিভ বার্নেস

11

না! - সাধারণভাবে নয় - একক শ্রেণির কোড ব্যতীত ব্যতিক্রমগুলি ভাল প্রবাহ নিয়ন্ত্রণ অনুশীলন হিসাবে বিবেচিত হয় না। ব্যতিক্রমকে যুক্তিসঙ্গত বা আরও ভাল হিসাবে বিবেচনা করা হয় এমন এক জায়গা, শর্তের সংকেত দেওয়ার উপায় হ'ল জেনারেটর বা পুনরুক্তি ব্যবস্থা। এই ক্রিয়াকলাপগুলি বৈধ ফলাফল হিসাবে যে কোনও সম্ভাব্য মান ফিরিয়ে দিতে পারে যাতে একটি সমাপ্তির সংকেত দেওয়ার জন্য একটি প্রক্রিয়া প্রয়োজন।

একসাথে এক বাইট স্ট্রিমের বাইনারি ফাইল পড়ার বিষয়টি বিবেচনা করুন - একেবারে কোনও মানই সম্ভাব্য বৈধ ফলাফল তবে আমাদের এখনও ফাইলের শেষের সংকেত দেওয়া দরকার। সুতরাং আমাদের একটি পছন্দ আছে, প্রতিবার দুটি মান, (বাইট মান এবং একটি বৈধ পতাকা) ফেরত দিন বা যখন আর কিছু করার নেই তখন একটি ব্যতিক্রম বাড়াতে হবে। দুটি ক্ষেত্রে গ্রাহক কোডটি দেখতে পাবেন:

# Using validity flag
valid, val = readbyte(source)
while valid:
    processbyte(val)
    valid, val = readbyte(source)
tidy_up()

অন্যথায়:

# With exceptions
try:
   val = readbyte(source)
   processbyte(val) # Note if a problem occurs here it will also raise an exception
except Exception: # Use a specific exception here!
   tidy_up()

তবে এটি যেহেতু পিইপি 343 বাস্তবায়িত হয়েছিল এবং ব্যাক পোর্ট করা হয়েছে, সমস্ত withবিবৃতি ঝরঝরে করে মুড়ে ফেলা হয়েছে । উপরেরটি খুব পাইথোনিক হয়:

with open(source) as input:
    for val in input.readbyte(): # This line will raise a StopIteration exception an call input.__exit__()
        processbyte(val) # Not called if there is nothing read

অজগর 3 এ এটি হয়ে গেছে:

for val in open(source, 'rb').read():
     processbyte(val)

আমি আপনাকে দৃ P়ভাবে পিইপি 343 পড়ার জন্য অনুরোধ করছি যা পটভূমি, যুক্তি, উদাহরণ ইত্যাদি দেয় read

সমাপ্তির সংকেত দেওয়ার জন্য জেনারেটর ফাংশনগুলি ব্যবহার করার সময় প্রক্রিয়াটির শেষের সিগন্যাল করার জন্যও একটি ব্যতিক্রম ব্যবহার করা স্বাভাবিক।

, পরের ম্যাচে ফিরে, এবং একটি উত্থাপন তারপর substituent কল আমি যোগ করতে, এই ধরনের ফাংশন জেনারেটর হওয়া উচিত, প্রথম কল প্রথম ম্যাচে ফেরার আপনার অন্বেষী উদাহরণ প্রায় অবশ্যই পিছন হয় চাই NotFoundযখন নেই ব্যতিক্রম আরও ম্যাচ।


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

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

আরে @ স্টিভবার্নস, শূন্য ত্রুটি ধরা উদাহরণের দ্বারা ভাগ করা দুর্বল প্রোগ্রামিংয়ের মতো শোনাচ্ছে (একটি সাধারণ-সাধারণ ক্যাচ যা ব্যতিক্রমটিকে পুনরায় উত্থাপন করে না)।
wtyeRogers

আপনার উত্তরটি এটিকে ফুটে উঠেছে বলে মনে হচ্ছে: ক) "না!" খ) ব্যতিক্রমগুলির মাধ্যমে নিয়ন্ত্রণ প্রবাহ বিকল্পের তুলনায় আরও ভাল কাজ করে তার কার্যকর উদাহরণ রয়েছে সি) জেনারেটরগুলি ব্যবহার করার জন্য প্রশ্নের কোডের সংশোধন (যা নিয়ন্ত্রণ প্রবাহ হিসাবে ব্যতিক্রমগুলি ব্যবহার করে এবং খুব ভাল করে)। যদিও আপনার উত্তরটি সাধারণত তথ্যবহুল, তবুও আইটেম এটিকে সমর্থন করার জন্য কোনও যুক্তি উপস্থিত রয়েছে বলে মনে হয় না, যা প্রাথমিকভাবে বিবৃত এবং আপাতদৃষ্টিতে প্রাথমিক বক্তব্য থাকার কারণে আমি কিছুটা সমর্থন আশা করব expect যদি এটি সমর্থিত হয়, তবে আমি আপনার উত্তরকে হ্রাস করব না। হায়রে ...
wTyeRogers
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.