পাঠ ৫৫ · ৫৮-এর মধ্যে · মডিউল ১৩
Home / Courses / Concepts of Programming Languages & Compiler Design / Python কেস স্টাডি

কেস স্টাডি: Python-এর ডিজাইন সিদ্ধান্ত

Case study: Python's design choices
১০ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • Python-এর ডায়নামিক টাইপিং ও ডাক টাইপিং দর্শন, এবং কেন এটি লেখার সহজতাকে অগ্রাধিকার দেয়
  • Python বাস্তবে কীভাবে M12/L54-এর রেফারেন্স-সাইকেল সমস্যা সমাধান করে
  • Python কীভাবে একই ভাষায় ইম্পারেটিভ, OOP ও ফাংশনাল স্টাইল একসাথে সমর্থন করে
  • LEGB স্কোপিং নিয়ম — M8/L38-এর স্ট্যাটিক স্কোপিং-এর একটি বাস্তব, দৈনন্দিন উদাহরণ

১ · ডায়নামিক টাইপিং ও ডাক টাইপিং

Python ডায়নামিক টাইপডM7/L32-এ বিস্তারিত — একটি ভ্যারিয়েবলের টাইপ কম্পাইল-টাইমে নয়, রানটাইমে তার বর্তমান মানের উপর ভিত্তি করে নির্ধারিত হয়। — কোনো ভ্যারিয়েবল ঘোষণার সময় টাইপ লিখতে হয় না। এই সিদ্ধান্ত থেকেই সরাসরি জন্ম নেয় Python-এর সিগনেচার দর্শন: ডাক টাইপিংDuck Typing"যদি এটি হাঁসের মতো হাঁটে ও হাঁসের মতো ডাকে, তাহলে এটিকে হাঁস ধরে নাও" — একটি অবজেক্টের কোনো অপারেশনের জন্য উপযুক্ততা নির্ধারিত হয় সে রানটাইমে প্রয়োজনীয় মেথড/অপারেশন সাপোর্ট করে কিনা তা দিয়ে, তার ঘোষিত টাইপ দিয়ে নয়। — ডায়নামিক টাইপিং (M7/L32)-এর একটি সরাসরি, বাস্তব ফলাফল, যা লেখার সহজতা/নমনীয়তাকে (M1/L04-এর writability) অগ্রাধিকার দেয়, কিছুটা compile-time এরর-ধরার ক্ষমতার বিনিময়ে।

২ · গার্বেজ কালেকশন — L54-এর সমস্যার প্রকৃত সমাধান

M12/L54-এ আমরা দেখেছি বিশুদ্ধ রেফারেন্স কাউন্টিং কখনোই একটি রেফারেন্স সাইকেল রিক্লেইম করতে পারে না। Python বাস্তবে এই সমস্যা রেফারেন্স কাউন্টিং-কে প্রাইমারি কৌশল হিসেবে রেখে, তার সাথে একটি সম্পূরক সাইকেল-ডিটেক্টর যোগ করে সমাধান করে — এই সাইকেল ডিটেক্টর পর্যায়ক্রমে চলে, ঠিক সেই ধরনের পারস্পরিক-রেফারেন্সিং গ্রুপ খুঁজে বের করে যা L54-এর কোড সেলে হাতে-কলমে তৈরি করা হয়েছিল, এবং সেগুলোকে বিশেষভাবে রিক্লেইম করে।

৩ · মাল্টি-প্যারাডাইম প্রকৃতি

M2/L10-এর মাল্টি-প্যারাডাইম থিমের একটি বাস্তব, প্রত্যক্ষ উদাহরণ — Python একই সাথে ইম্পারেটিভ (M2/L06 — সাধারণ লুপ ও স্টেট মিউটেশন), OOP (M2/L07 — class), এবং ফাংশনাল (M2/L08 — lambda, map/filter, ফার্স্ট-ক্লাস ফাংশন) স্টাইল স্বচ্ছন্দে সমর্থন করে — প্রোগ্রামার সমস্যার প্রকৃতি অনুযায়ী স্টাইল বেছে নিতে পারে।

৪ · LEGB স্কোপিং

Python-এর স্কোপ রেজোলিউশন নিয়ম — LEGB (Local, Enclosing, Global, Built-in) — একটি নাম খুঁজতে প্রথমে স্থানীয় স্কোপ, তারপর ঘিরে-থাকা (enclosing) ফাংশন স্কোপ, তারপর গ্লোবাল স্কোপ, তারপর built-in স্কোপ দেখে — এটি M8/L38-এর স্ট্যাটিক (লেক্সিক্যাল) স্কোপিং-এর "বাইরের দিকে খুঁজে যাওয়া" নীতিরই একটি নির্দিষ্ট, নামযুক্ত বাস্তব রূপ — প্রতিদিন Python লেখা প্রোগ্রামাররা এই নিয়মের উপর নির্ভর করেন, প্রায়ই এই কোর্সের পরিভাষা না জেনেই।

এই কোর্সের সাথে সামগ্রিক সংযোগ

Python-এর প্রায় প্রতিটি প্রধান ডিজাইন সিদ্ধান্ত এই কোর্সের কোনো না কোনো নির্দিষ্ট মডিউলের একটি বাস্তব উদাহরণ — এটাই এই কেস স্টাডির মূল উদ্দেশ্য: তত্ত্ব শুধু বিমূর্ত ধারণা নয়, বাস্তব ভাষার প্রতিটি "অনুভূতি"-র (feel) পেছনে একটি সুনির্দিষ্ট, নামযুক্ত ডিজাইন সিদ্ধান্ত আছে।

৫ · বাস্তবায়ন — একটি বাস্তব ডাক টাইপিং ডেমো

নিচের কোড সেলে দুটো সম্পূর্ণ অসম্পর্কিত ক্লাস — কোনো কমন বেস ক্লাস নেই, কোনো টাইপ ঘোষণা নেই — শুধু কাকতালীয়ভাবে উভয়েরই .quack() মেথড আছে। make_it_quack ফাংশন কখনো তাদের টাইপ পরীক্ষা করে না, শুধু কল করার চেষ্টা করে — এটাই ডাক টাইপিং হাতেকলমে।

Python
class Duck:
    def quack(self):
        return "কোয়াক! আমি একটি হাঁস।"

class ToyDuck:
    """সম্পূর্ণ অসম্পর্কিত ক্লাস -- Duck-এর সাথে কোনো বেস ক্লাস শেয়ার করে না,
    কোনো টাইপ ঘোষণাও নেই -- শুধু কাকতালীয়ভাবে একই .quack() মেথড আছে।"""
    def quack(self):
        return "স্কুইক-কোয়াক! আমি একটি খেলনা হাঁস।"

class Person:
    """এই ক্লাসের quack() নেই -- ডাক টাইপিং এটিকে রিজেক্ট করবে।"""
    def speak(self):
        return "আমি একজন মানুষ, আমি quack করতে পারি না।"

def make_it_quack(thing):
    """ডাক টাইপিং: thing-এর ঘোষিত টাইপ কী তা কখনো পরীক্ষা করা হয় না --
    শুধু thing.quack() আছে কিনা এবং কল করা যায় কিনা, তাই গুরুত্বপূর্ণ।"""
    return thing.quack()

print("ডাক টাইপিং ডেমো:")
for obj in [Duck(), ToyDuck()]:
    print(f"  {type(obj).__name__}.quack() -> {make_it_quack(obj)}")

print("\nPerson-এর জন্য চেষ্টা করলে (quack() নেই):")
try:
    make_it_quack(Person())
except AttributeError as e:
    print("  AttributeError সঠিকভাবে ধরা পড়ল:", e)


print("\n\nPython-এর ডিজাইন সিদ্ধান্ত <-> এই কোর্সের পাঠ:")
design_choices = {
    "ডায়নামিক টাইপিং + ডাক টাইপিং": {
        "কোর্স রেফারেন্স": "M7/L32",
        "উদাহরণ": "উপরের make_it_quack() -- কোনো টাইপ ঘোষণা ছাড়াই কাজ করে",
    },
    "প্রাইমারি GC = রেফারেন্স কাউন্টিং + সাইকেল ডিটেক্টর": {
        "কোর্স রেফারেন্স": "M12/L54",
        "উদাহরণ": "CPython-এর gc মডিউল L54-এর সাইকেল সমস্যার বাস্তব সমাধান",
    },
    "মাল্টি-প্যারাডাইম": {
        "কোর্স রেফারেন্স": "M2/L10",
        "উদাহরণ": "class + lambda/map/filter + সাধারণ ইম্পারেটিভ কোড, সব একসাথে",
    },
    "LEGB স্কোপিং": {
        "কোর্স রেফারেন্স": "M8/L38",
        "উদাহরণ": "Local -> Enclosing -> Global -> Built-in, স্ট্যাটিক স্কোপিং-এরই একটি নামকরণ",
    },
}
for choice, info in design_choices.items():
    print(f"\n  {choice}")
    print(f"    কোর্স রেফারেন্স : {info['কোর্স রেফারেন্স']}")
    print(f"    উদাহরণ         : {info['উদাহরণ']}")

    
লক্ষ্য করুন — make_it_quack ফাংশনটি Duck বা ToyDuck কোনোটির টাইপই কখনো পরীক্ষা করেনি (কোনো isinstance() চেক নেই), তবুও উভয়ের জন্যই সঠিকভাবে কাজ করেছে। কিন্তু Person-এর জন্য চেষ্টা করলে AttributeError আসে — কারণ রানটাইমে দেখা যায় প্রয়োজনীয় .quack() মেথডই নেই। এটাই ডাক টাইপিং-এর মূল কথা — "টাইপ কী" নয়, "কী করতে পারে" গুরুত্বপূর্ণ, এবং সেই যাচাই রানটাইমেই ঘটে, কম্পাইল-টাইমে নয়।
মূল কথা · Key takeaway

Python-এর "অনুভূতি" — নমনীয়, লেখা সহজ, কিন্তু কম compile-time গ্যারান্টি — এই কোর্সের নির্দিষ্ট ডিজাইন সিদ্ধান্তগুলোর প্রত্যক্ষ ফল: ডায়নামিক টাইপিং (M7), রেফারেন্স-কাউন্টিং-ভিত্তিক GC (M12), মাল্টি-প্যারাডাইম নমনীয়তা (M2), এবং সহজবোধ্য LEGB স্কোপিং (M8) — কোনোটিই কাকতালীয় নয়, প্রতিটিই একটি সচেতন ট্রেড-অফ।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ ডাক টাইপিং-এর সুবিধা হলো নমনীয়তা। এর কী মূল্য দিতে হয় — কোন সমস্যাটি ডাক টাইপিং কম্পাইল-টাইমে ধরতে পারে না যা স্ট্যাটিক টাইপিং পারে?

একটি অবজেক্টে প্রয়োজনীয় মেথড নেই — এই ভুলটি ডাক টাইপিং-এ কেবল রানটাইমে, যখন সেই নির্দিষ্ট কোড পাথ আসলে এক্সিকিউট হয়, তখনই ধরা পড়ে (উপরের কোড সেলে Person-এর AttributeError ঠিক এভাবেই এসেছে)। স্ট্যাটিক টাইপিং-এ (M7/L32) এই একই ভুল প্রোগ্রাম রান করার আগেই, কম্পাইল-টাইমেই ধরা পড়ত — এটাই M1/L04-এর writability বনাম reliability ট্রেড-অফের একটি বাস্তব উদাহরণ।

প্র ০২ Python যদি শুধু রেফারেন্স কাউন্টিং ব্যবহার করত, সাইকেল ডিটেক্টর ছাড়া, তাহলে দীর্ঘ সময় ধরে চলা একটি প্রোগ্রামে কী সমস্যা হতে পারত?

M12/L54-এর কোড সেলে দেখানো সাইকেল-সমস্যাটি ধীরে ধীরে জমতে থাকত — প্রতিটি নতুন তৈরি হওয়া, কিন্তু আর reachable না হওয়া অবজেক্ট-সাইকেল স্থায়ীভাবে মেমরিতে আটকে থাকত, কারণ তাদের ref_count কখনো শূন্যে পৌঁছাত না। দীর্ঘ সময় ধরে চলা একটি প্রোগ্রামে (যেমন একটি ওয়েব সার্ভার) এটি ধীরে ধীরে মেমরি ব্যবহার বাড়িয়ে দিত — কার্যত একটি মেমরি লিক তৈরি করত, যদিও প্রোগ্রামার কোনো ম্যানুয়াল মেমরি ভুল করেনি।

প্র ০৩ LEGB-এর "E" (Enclosing) অংশটি M8-এর কোন নির্দিষ্ট পাঠের সাথে সবচেয়ে সরাসরি সম্পর্কিত, এবং কেন?

M8/L40-এর ক্লোজার ও লেক্সিক্যাল এনভায়রনমেন্ট-এর সাথে সরাসরি সম্পর্কিত। "Enclosing" স্কোপ মানে একটি ভেতরের (nested) ফাংশনের বাইরের ফাংশনের স্কোপ — একটি ক্লোজার ঠিক এই enclosing স্কোপের ভ্যারিয়েবলগুলো "মনে রেখে" দেয়, এমনকি বাইরের ফাংশন রিটার্ন করে যাওয়ার পরেও। LEGB-এর E ধাপটি তাই ক্লোজার বাস্তবায়নের স্কোপ-লুকআপ নিয়মেরই একটি নাম।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে যদি আরেকটি ক্লাস Airplane তৈরি করা হয় যার একটি .quack() মেথড আছে যা "vroom!" রিটার্ন করে, তাহলে make_it_quack(Airplane()) কী করবে?

    এটি সফলভাবে "vroom!" রিটার্ন করবে, কোনো এরর ছাড়াই — এটাই ডাক টাইপিং-এর সবচেয়ে চরম উদাহরণ: একটি "প্লেন" আক্ষরিক অর্থে "হাঁসের মতো ডাকতে" পারবে যদি তার একটি .quack() মেথড থাকে, তা যত অস্বাভাবিকই শোনাক না কেন। এটি দেখায় ডাক টাইপিং সম্পূর্ণভাবে অন্ধ concept/semantic সম্পর্কের প্রতি — শুধু মেথডের উপস্থিতি ও কলযোগ্যতাই গুরুত্বপূর্ণ, বাস্তব "অর্থ" নয়।

  2. পরীক্ষা করুন: উপরের কোড সেলে make_it_quack-এর ভেতরে Person-এর জন্য try/except বাদ দিয়ে সরাসরি কল করলে কী এরর মেসেজ আসে তা লক্ষ্য করে দেখুন।

    AttributeError: 'Person' object has no attribute 'quack' ধরনের একটি মেসেজ আসবে — Python রানটাইমে অবজেক্টের অ্যাট্রিবিউট/মেথড টেবিল দেখে এবং quack নামে কিছু না পেয়ে এই এরর তোলে। এই এরর মেসেজটিই প্রমাণ করে টাইপ-চেকিং এখানে সম্পূর্ণ রানটাইম ঘটনা — কোনো আলাদা কম্পাইল-টাইম টাইপ-চেকিং ধাপ (M6/L30) নেই যা আগেভাগে এই সমস্যা ধরতে পারত।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
রানটাইম এনভায়রনমেন্ট ও গার্বেজ কালেকশন