সরাসরি পর্বে যান
পর্ব ০৮ · ৫৫-এর মধ্যে · মডিউল ২

অপারেটর — তুলনা, যুক্তি, walrus আর is বনাম ==

Python Operators — Comparison, Logic, Walrus & “is” vs “==”

Taught in Bangla, this lesson explains Python operators: comparisons and chaining, and/or/not with short-circuit evaluation, identity with is versus equality with ==, membership with in, assignment operators and the walrus operator, bitwise operators, precedence and the conditional expression.

এই পর্বে যা শিখবেন

  • তুলনা অপারেটর, chaining, আর ভিন্ন type বা string তুলনার নিয়ম
  • and, or, not কীভাবে ধাপে ধাপে হিসাব হয়, আর কেন এরা operand ফেরত দেয়
  • is বনাম == — identity আর equality-র পার্থক্য, আর x is None কেন সঠিক রীতি
  • in, +=, walrus := আর bitwise অপারেটর — কোথায় কাজে লাগে, কোথায় ফাঁদ
  • অপারেটরের অগ্রাধিকার, আর AI-এর লেখা কোডে অপারেটর-সংক্রান্ত সাধারণ ভুল চেনা

১ · অপারেটর মানে প্রশ্ন — আর প্রতিটি প্রশ্নের উত্তর একটি মান

আগের পর্বে আপনি সংখ্যা নিয়ে হিসাব করেছেন — +, -, *, ভাগের দুই রূপ / আর //, ভাগশেষ % আর ঘাত **. ওগুলো হলো গাণিতিক অপারেটর: দুটি সংখ্যা নিয়ে নতুন একটি সংখ্যা বানায়। এই পর্বে আমরা বাকি অপারেটরগুলো দেখব, আর সেগুলোর কাজ একটু অন্যরকম। এরা মূলত প্রশ্ন করে.

ভাবুন, একটি রেল স্টেশনের টিকিট কাউন্টারে কর্মী প্রতিটি যাত্রীকে মনে মনে কয়েকটি প্রশ্ন করেন: “বয়স কি ৫-এর কম?”, “ছাত্র কার্ড আছে এবং বয়স ২৫-এর নিচে?”, “এই টিকিট কি সেই একই টিকিট, যা আগে স্ক্যান হয়েছে?” প্রোগ্রামও ঠিক এভাবে সিদ্ধান্ত নেয়। প্রশ্ন করার যন্ত্রগুলোই হলো operator, আর যাদের নিয়ে প্রশ্ন করা হয় তারা operand. age < 5-এ < হলো operator, age আর 5 হলো দুটি operand.

মূল ধারণা

Python-এ প্রতিটি অপারেটর-যুক্ত লেখা একটি expression — অর্থাৎ হিসাব শেষে এটি একটি মান হয়ে যায়। 3 + 4 হয়ে যায় 7; 3 < 4 হয়ে যায় True. প্রশ্নের উত্তরও একটি সাধারণ object, যাকে নাম দেওয়া যায়, print করা যায়, ফাংশনে পাঠানো যায়: is_child = age < 5. এই ছবিটা মাথায় রাখলে পুরো পর্ব সহজ হয়ে যাবে।

তুলনার উত্তর সাধারণত bool type-এর — True অথবা False. পর্ব ০৬-এ দেখেছেন, Python-এ bool আসলে int-এর একটি বিশেষ রূপ: True মানে 1, False মানে 0. তাই True + True লিখলে 2 পান। এটা কাজে লাগে — যেমন কয়টি শর্ত সত্য তা গুনতে — তবে নতুনদের জন্য বিভ্রান্তিকরও হতে পারে। আর পর্ব ০৬-এর truthiness-এর কথাও মনে রাখুন: শূন্য, ফাঁকা string "", ফাঁকা list [], ফাঁকা dict আর None — এরা “মিথ্যার মতো” (falsy); বাকি প্রায় সবকিছু “সত্যের মতো” (truthy). অংশ ৩-এ এর পুরো শক্তি দেখবেন।

পরিবারঅপারেটরকী প্রশ্ন বা কাজকোথায়
গাণিতিক+ - * / // % **নতুন সংখ্যা বানাওপর্ব ০৭
তুলনা== != < <= > >=সমান? বড়? ছোট?অংশ ২
যুক্তি (logical)and or notদুটোই? যেকোনো একটি? উল্টো?অংশ ৩
পরিচয় (identity)is, is notএকই object?অংশ ৪
সদস্যতা (membership)in, not inভেতরে আছে?অংশ ৫
Assignment= += -= … :=নাম দাও / বদলাওঅংশ ৬
Bitwise& | ^ ~ << >>বিট ধরে কাজঅংশ ৭

২ · তুলনা — বড়, ছোট, সমান আর গণিতের মতো chaining

ছয়টি comparison operator আছে: == (সমান), != (সমান নয়), <, <=, >, >=. একটি বিষয় শুরুতেই পরিষ্কার করে নিন — = আর == সম্পূর্ণ আলাদা। একটি = মানে “এই নামটি ওই object-এ লাগাও” (একটি আদেশ); দুটি == মানে “এ দুটির মান কি সমান?” (একটি প্রশ্ন)। Python-এ if x = 5: লিখলে সরাসরি SyntaxError আসে, তাই C-র মতো ভুল করে assignment হয়ে যাওয়ার ভয় নেই।

Chaining — বয়স দিয়ে টিকিটের দাম

ধরুন একটি চিড়িয়াখানার টিকিটের নিয়ম: ৫ বছরের কম হলে ফ্রি, ৫ থেকে ১৭ হলে ৩০ টাকা, ১৮ থেকে ৫৯ হলে ৬০ টাকা, ৬০ বা তার বেশি হলে আবার ৩০ টাকা। গণিতের খাতায় আপনি লিখতেন 18 ≤ age < 60. Python-এ হুবহু তা-ই লেখা যায়:

is_adult = 18 <= age < 60      # প্রাপ্তবয়স্ক, কিন্তু প্রবীণ নন
is_child = 5 <= age < 18

একে বলে chained comparison. Python এটিকে পড়ে “18 <= age এবং age < 60” হিসেবে — তবে মাঝের age মাত্র একবার হিসাব করে। মাঝখানে যদি কোনো ধীর ফাংশন ডাক থাকে, যেমন 1 < get_score() < 10, তাহলে ফাংশনটি একবারই চলবে। নিচের ডায়াগ্রামে ধাপগুলো দেখুন:

আপনি লিখলেনগণিতের খাতার মতো
x = 5
print(1 < x < 10)
Python যেভাবে পড়েদুটি তুলনা, মাঝে and
(1 < x) and (x < 10)
# কিন্তু x-এর মান আনা হয় একবারই
ধাপ: বাম তুলনাx = 5
1 < 5  →  True
# True, তাই পরের তুলনায় যেতে হবে
ধাপ: ডান তুলনাসেই একই 5
5 < 10  →  True
ফলাফল: True
যদি x = 0 হতোশর্টকাট
1 < 0  →  False
# ডান দিক আর দেখাই হয় না
ফলাফল: False
  1. লেখা। 1 < x < 10 — গণিতে এর অর্থ “x, 1 আর 10-এর মাঝখানে”। Python-এও ঠিক সেই অর্থ।
  2. Python-এর পড়া। ভেতরে এটি হয়ে যায় 1 < x and x < 10, একটি পার্থক্যসহ — x (বা মাঝের যেকোনো expression) মাত্র একবার হিসাব হয়। কোনো ফাংশন ডাক থাকলে সেটি দুবার চলবে না।
  3. বাম তুলনা। আগে 1 < 5 দেখা হয় — True. যেহেতু and-এর প্রথম অংশ সত্য, উত্তর জানতে দ্বিতীয় অংশটিও দেখতে হবে।
  4. ডান তুলনা। আগে আনা সেই 5 দিয়েই 5 < 10 — True. দুটোই সত্য, তাই পুরো expression-এর মান True.
  5. শর্টকাট। যদি x হতো 0, তাহলে 1 < 0 মিথ্যা — আর and-এর একটি অংশ মিথ্যা হলেই পুরোটা মিথ্যা। তাই Python ডান দিকের তুলনা আর করেই না। এই “অলসতা”র নাম short-circuit, অংশ ৩-এ বিস্তারিত।

C, Java বা JavaScript-এ 1 < x < 10 লিখলে অর্থ দাঁড়ায় (1 < x) < 10 — প্রথম তুলনার ফল (0 বা 1, অথবা true/false) 10-এর সাথে তুলনা হয়, যা প্রায় সবসময় সত্য। অর্থাৎ x = 500 হলেও শর্তটি পাস করে যায়! অন্য ভাষা থেকে কোড অনুবাদের সময় বা AI-কে অন্য ভাষার কোড Python-এ বদলাতে বললে এই পার্থক্য মাথায় রাখুন।

ভিন্ন type-এর তুলনা

  • 1 == 1.0 → True. সংখ্যার type আলাদা হলেও মান তুলনা হয় — int, float আর bool একে অপরের সাথে তুলনীয়। (তবে float-এর সূক্ষ্ম ফাঁদ, যেমন 0.1 + 0.2 == 0.3 কেন False, পর্ব ০৭-এ দেখেছেন।)
  • "5" == 5 → False. একটি লেখা, একটি সংখ্যা — Python নিজে থেকে type বদলায় না। input() থেকে আসা মান সবসময় string, তাই input() == 5 কখনো সত্য হবে না (পর্ব ১০)।
  • "5" < 5 → TypeError. সমান-অসমান প্রশ্ন যেকোনো দুটি object-কে করা যায়, কিন্তু “কে বড়” প্রশ্ন string আর সংখ্যার মধ্যে অর্থহীন। Python 3 তাই সরাসরি error দেয়: '<' not supported between instances of 'str' and 'int'. (Python 2 এখানে চুপচাপ একটি অদ্ভুত ক্রম ধরে নিত — Python 3 সেই ঝুঁকি সরিয়েছে।)

String-এর তুলনা — অক্ষর ধরে ধরে, code point দিয়ে

String তুলনা হয় অভিধানের মতো, বাম থেকে এক অক্ষর এক অক্ষর করে: প্রথম যেখানে পার্থক্য, সেখানেই সিদ্ধান্ত। একে বলে lexicographic order. কিন্তু “কোন অক্ষর বড়” তা ঠিক হয় অক্ষরের code point দিয়ে — Unicode-এ প্রতিটি অক্ষরের একটি নম্বর আছে, ord() দিয়ে দেখা যায়।

  • "apple" < "banana" → True (a আগে, b পরে) — প্রত্যাশিত।
  • "a" < "B" → False! কারণ ord("a") হলো 97, আর ord("B") হলো 66. সব বড় হাতের অক্ষর ছোট হাতের অক্ষরের আগে আসে।
  • "10" < "9" → True! কারণ প্রথম অক্ষর "1" আর "9"-এর তুলনাতেই সিদ্ধান্ত হয়ে যায়। CSV থেকে পড়া রোল নম্বর string হিসেবে সাজালে ১০ নম্বর রোল ৯-এর আগে চলে আসে — খুব পরিচিত বাগ।
  • "ক" < "খ" → True. বাংলা ব্যঞ্জনবর্ণের code point বর্ণমালার ক্রমেই সাজানো, তাই সাধারণ শব্দে ক্রম অনেকটা মেলে। কিন্তু কার-চিহ্ন, যুক্তাক্ষর আর ড়/য়-এর মতো অক্ষরে code point-এর ক্রম সবসময় অভিধানের ক্রম নয়। বাংলা নামের সঠিক বর্ণানুক্রম দরকার হলে আলাদা ব্যবস্থা লাগে — পরের পর্বে (string) Unicode নিয়ে আরও দেখবেন।

স্কুলের রোল কলের খাতাটি ভাবুন। শিক্ষক যদি নামের প্রথম অক্ষর দেখে সাজান, “Anik” আর “anik” আলাদা জায়গায় যাবে, কারণ কম্পিউটারের চোখে বড় হাতের A আর ছোট হাতের a দুটি আলাদা নম্বর। কেস না মেনে তুলনা করতে চাইলে দুই দিককেই একই রূপে আনুন: a.lower() == b.lower() (আরও নির্ভুলভাবে casefold(), পর্ব ০৯)।

একই নিয়ম list আর tuple-এর তুলনাতেও: [1, 2] < [1, 3] সত্য, কারণ প্রথম উপাদান সমান, দ্বিতীয়তে 2 < 3. এই কৌশলে (মার্ক, নাম) জোড়া সাজানো যায় — পর্ব ১৫-এ কাজে লাগবে।

৩ · যুক্তি — and, or, not আর short-circuit

একটি প্রশ্নে সব সময় কাজ চলে না। লগইনের জন্য লাগে “username ঠিক এবং password ঠিক”; ছাড়ের জন্য হয়তো “কার্ড আছে অথবা কেনাকাটা ১০০০ টাকার বেশি”। এই প্রশ্নগুলো জোড়া লাগায় তিনটি logical operator, যারা Python-এ ইংরেজি শব্দ — চিহ্ন নয়:

and — দুটোই

দুই দিক সত্য হলেই সত্য। username_ok and password_ok — একটিও ভুল হলে লগইন নেই।

একটি মিথ্যা → পুরোটা মিথ্যা

or — যেকোনো একটি

অন্তত এক দিক সত্য হলেই সত্য। has_card or total >= 1000 — যেকোনো একটি পূরণ হলেই ছাড়।

একটি সত্য → পুরোটা সত্য

not — উল্টো

একটি মানের truthiness উল্টে দেয়, আর সবসময় True বা False দেয়। not is_banned.

not "" → True

Short-circuit — উত্তর জানা হলেই থামা

বাস্তব যুক্তির একটি স্বাভাবিক নিয়ম আছে। bKash-এ টাকা পাঠানোর আগে অ্যাপ দেখে “PIN ঠিক এবং ব্যালান্স যথেষ্ট”। PIN-ই ভুল হলে ব্যালান্স দেখার দরকারই নেই — উত্তর ইতিমধ্যে “না”। Python ঠিক এভাবেই কাজ করে, একে বলে short-circuit evaluation:

  • A and B: A falsy হলে B দেখাই হয় না।
  • A or B: A truthy হলে B দেখাই হয় না।

এটা শুধু গতি বাঁচায় না, নিরাপত্তাও দেয়। count != 0 and total / count > 50 — count শূন্য হলে ভাগটি কখনো চলে না, তাই ZeroDivisionError আসে না। items and items[0] == "চা" — list ফাঁকা হলে items[0] চেষ্টাই হয় না। প্রথম অংশটি দ্বিতীয় অংশের পাহারাদার।

চমক: and/or ফল হিসেবে একটি operand-ই ফেরত দেয়

অনেক ভাষায় and/or-এর ফল সবসময় true বা false. Python-এ নয়। নিয়মটি খুব সরল — যে operand-এ গিয়ে উত্তর নিশ্চিত হলো, সেটিই ফেরত আসে:

  • or ফেরত দেয় প্রথম truthy মান; কোনোটিই truthy না হলে শেষ মানটি।
  • and ফেরত দেয় প্রথম falsy মান; সবাই truthy হলে শেষ মানটি।

নিচের ডায়াগ্রামে দুটি expression ধাপে ধাপে হিসাব হচ্ছে। প্রতিটি ধাপে খেয়াল করুন — Python কোন operand দেখল, আর কেন থামল বা এগোল:

10 or "" or "অতিথি"25 and 0 and 7
Python এখন যা দেখছে
operand
truthy?
সিদ্ধান্ত
  1. or-এর প্রথম operand: 0. শূন্য falsy. or-এর প্রশ্ন “অন্তত একটি সত্য?” — এখনো উত্তর অজানা, তাই পরেরটিতে যেতে হবে।
  2. দ্বিতীয় operand: "". ফাঁকা string-ও falsy. এখনো কোনো সত্য পাওয়া যায়নি — আরও এগোও।
  3. তৃতীয় operand: "অতিথি". খালি নয়, তাই truthy. or-এর উত্তর নিশ্চিত — আর ফল হিসেবে আসে True নয়, এই string-টিই: "অতিথি".
  4. and-এর প্রথম operand: 5. শূন্য নয়, truthy. and-এর প্রশ্ন “সবাই সত্য?” — একটি সত্য দিয়ে তা বলা যায় না, তাই এগোও।
  5. দ্বিতীয় operand: 0. Falsy! একটি মিথ্যা পেলেই and-এর উত্তর নিশ্চিত “না”। ফল হিসেবে আসে এই 0 নিজেই।
  6. 7 — দেখাই হলো না। Short-circuit: উত্তর আগেই নিশ্চিত, তাই 7 হিসাব করা হয়নি। এখানে 7-এর জায়গায় কোনো ফাংশন ডাক থাকলে সেটি চলতই না।

এই আচরণ থেকে এসেছে Python-এর একটি অতি প্রচলিত রীতি — ডিফল্ট মান দেওয়া:

display_name = user_input or "অতিথি"     # কিছু না লিখলে "অতিথি"

সাবধান: or দেখে truthiness, “মান আছে কি না” নয়। discount = user_discount or 10 লিখলে কেউ সত্যিই 0% ছাড় দিতে চাইলেও 10 হয়ে যাবে, কারণ 0 falsy. যেখানে 0, "" বা [] বৈধ মান, সেখানে স্পষ্ট লিখুন: “মান None হলে তবেই ডিফল্ট” — অংশ ৪-এর is None দিয়ে।

এবার সব একসাথে চালিয়ে দেখুন — chaining, ভিন্ন type, string তুলনা, and/or-এর ফেরত মান আর একটি ছাড়ের শর্ত:

age = 25
print(18 <= age < 60)              # গণিতের মতো chaining
print(1 == 1.0, "5" == 5)          # ভিন্ন type-এর তুলনা
print("a" < "B", ord("a"), ord("B"))
print("10" < "9", "ক" < "খ")       # string: অক্ষর ধরে ধরে

print(0 or "" or "অতিথি")          # প্রথম truthy মান
print(5 and 0 and 7)               # প্রথম falsy মান
print(not "", not "ঢাকা")

user_input = ""
name = user_input or "অতিথি"
print(f"স্বাগতম, {name}!")

has_card = True
total = 1200
print("ছাড় পাবেন?", has_card and total >= 1000)

প্রথম লাইন True — 25 সত্যিই 18 আর 60-এর মাঝে। এরপর True False: 1 আর 1.0 সমান মান, কিন্তু string "5" সংখ্যা 5-এর সমান নয়। তৃতীয় লাইনে False 97 66 — ছোট হাতের a-র code point বড় হাতের B-র চেয়ে বেশি, তাই "a" < "B" মিথ্যা। "10" < "9" সত্য, কারণ প্রথম অক্ষরেই '1' < '9'. তারপর অতিথি আর 0 — ডায়াগ্রামের মতোই। not "" হলো True, not "ঢাকা" হলো False — not সবসময় খাঁটি bool দেয়। ফাঁকা user_input-এর জায়গায় এসেছে "অতিথি", আর শেষ লাইনে দুটি শর্তই সত্য, তাই and ফেরত দিয়েছে শেষ operand total >= 1000-এর মান True.

৪ · is বনাম == — একই জিনিস, নাকি দেখতে এক?

পর্ব ০৫-এ শিখেছেন, প্রতিটি object-এর একটি value আর একটি identity আছে, আর নাম হলো object-এর গায়ে লাগানো লেবেল। সেই ছবি থেকেই দুটি আলাদা প্রশ্ন আসে:

  • a == b — equality: “দুটির মান কি সমান?”
  • a is b — identity: “দুটি নাম কি একই object-এ লাগানো?”

ঢাকার নিউমার্কেটের একই দোকান থেকে দুই বান্ধবী হুবহু একই রঙের, একই ডিজাইনের দুটি শাড়ি কিনলেন। শাড়ি দুটি “সমান” (== সত্য) — কিন্তু “একই শাড়ি” নয় (is মিথ্যা); একটিতে চায়ের দাগ লাগলে অন্যটিতে লাগবে না। আবার মা যে শাড়িকে বলেন “লাল শাড়িটা”, মেয়ে সেটিকেই বলে “ঈদের শাড়ি” — দুটি নাম, একটিই শাড়ি (is সত্য)।

1a = [1, 2, 3]2b = [1, 2, 3]3print(a == b, a is b)4c = a5c.append(4)6print(a is c, a == b)
নাম → object
a
b
c
list #1
list #2
  1. প্রথম list. [1, 2, 3] লেখাটি একটি নতুন list object বানাল (list #1), a লেবেল তাতে লাগল।
  2. দ্বিতীয় list — আলাদা object. একই লেখা আবার লিখলেও প্রতিটি [ ... ] একটি নতুন list বানায়। b লাগল list #2-এ। মান হুবহু এক, কিন্তু দুটি আলাদা জিনিস।
  3. a == b সত্য, a is b মিথ্যা. == ভেতরের উপাদান মিলিয়ে দেখে — সব সমান। is শুধু দেখে লেবেল দুটি একই object-এ কি না — না।
  4. c = a — নতুন object নয়। c লেবেলটি লাগল সেই list #1-এর গায়েই। এখন a আর c একই object-এর দুটি নাম, অর্থাৎ alias.
  5. c.append(4). List #1-এর ভেতরে 4 যোগ হলো। a দিয়ে দেখলেও এখন [1, 2, 3, 4]. List #2 যেমন ছিল তেমনই।
  6. a is c সত্য, a == b এখন মিথ্যা. একই object, তাই is সত্য। আর list #1 বদলে যাওয়ায় list #2-এর সাথে মান আর মেলে না। মনে রাখুন: is সত্য হলে == প্রায় সবসময় সত্য; কিন্তু == সত্য হলে is সত্য হবেই, এমন নয়।

x is None — is-এর আসল কাজ

তাহলে is কোথায় ব্যবহার করবেন? মূলত একটি জায়গায়: None-এর সাথে তুলনায়। পুরো Python প্রোগ্রামে None object মাত্র একটি — একে বলে singleton. তাই “এটি কি None?” প্রশ্নের নিখুঁত উত্তর হলো identity দেখা: if result is None: আর উল্টোটা if result is not None:. Python-এর অফিসিয়াল স্টাইল গাইড PEP 8 স্পষ্ট বলে: None-এর মতো singleton-এর সাথে তুলনা সবসময় is বা is not দিয়ে, কখনো == দিয়ে নয়।

কেন == None নয়? কারণ ==-এর অর্থ object নিজে ঠিক করতে পারে — যেকোনো class নিজের “সমান”-এর নিয়ম লিখতে পারে (পর্ব ৩০)। NumPy array-তে arr == None একটি একক True/False দেয় না, প্রতিটি উপাদানের জন্য আলাদা ফলের একটি array দেয়। is-এর অর্থ কেউ বদলাতে পারে না — তাই এটি নিরাপদ, আর একটু দ্রুতও। আর not x is None না লিখে x is not None লিখুন — পড়তে স্বাভাবিক, অর্থও দ্ব্যর্থহীন।

সংখ্যা আর string-এ is কেন অবিশ্বস্ত

এখানে একটি ফাঁদ আছে, যা নতুনদের প্রায়ই বিভ্রান্ত করে। REPL-এ চেষ্টা করলে দেখবেন, কখনো কখনো সমান সংখ্যার জন্য is সত্য হয়, কখনো মিথ্যা। কারণ CPython মেমরি বাঁচাতে কিছু object আগে থেকে বানিয়ে রাখে আর বারবার ব্যবহার করে:

  • Small int cache: −5 থেকে 256 পর্যন্ত int-গুলো CPython শুরুতেই তৈরি করে রাখে। তাই int("100") is int("100") সত্য, কিন্তু int("1000") is int("1000") মিথ্যা — দুটি আলাদা 1000 object তৈরি হয়।
  • String interning: নামের মতো দেখতে কিছু ছোট string (যেমন "hello") CPython একটিমাত্র কপিতে রাখে। কিন্তু চলার সময় জোড়া লাগানো বা ইনপুট থেকে আসা string সাধারণত আলাদা object.
  • একই ফাইলের constant: একই কোড-অংশে দুবার 1000 লিখলে compiler অনেক সময় দুটিকে একই object বানিয়ে ফেলে — তখন আবার is সত্য!

এগুলো সবই CPython-এর ভেতরের সুবিধা, ভাষার প্রতিশ্রুতি নয়। Version বদলালে, PyPy-তে চালালে বা কোড একটু অন্যভাবে লিখলে ফল বদলে যেতে পারে। তাই নিয়ম একটাই: মান তুলনায় সবসময় ==; is শুধু None (আর True/False-এর মতো নিশ্চিত singleton-এর ক্ষেত্রে, যা খুব কম লাগে)। Python 3.8 থেকে is-এর পাশে সরাসরি লেখা মান (literal) দেখলে compiler নিজেই সতর্ক করে — একটি SyntaxWarning. Python 3.14-এ x is 5 লেখা একটি ফাইল চালালে stderr-এ আসে:

prog.py:2: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
  print(x is 5)

লক্ষ করুন, এটি warning — প্রোগ্রাম থামে না, আর এই ক্ষেত্রে ফল True-ও আসে (5 cache-এ আছে)। ঠিক এজন্যই বাগটি বিপজ্জনক: পরীক্ষায় কাজ করে, পরে কোনো একদিন বড় সংখ্যায় হঠাৎ ভুল ফল দেয়। Warning দেখলেই is বদলে == করুন।

৫ · in আর not in — ভেতরে আছে কি?

Membership operator in জিজ্ঞেস করে “বাম দিকের জিনিসটি কি ডান দিকের সংগ্রহের ভেতরে আছে?” প্রশ্নটি একই, কিন্তু “ভেতরে থাকা”র অর্থ সংগ্রহের ধরন অনুযায়ী একটু বদলায়:

ডান দিকেin কী খোঁজেউদাহরণ
stringটুকরো লেখা (substring)"ঢাকা" in "ঢাকা বিভাগ" → True
list / tupleসমান (==) কোনো উপাদান4 in [1, 2, 3] → False
dictশুধু key, value নয়"রহিম" in marks → True, 80 in marks → False
setসমান উপাদান (খুব দ্রুত)"bKash" in methods

in সবচেয়ে সুন্দর কাজ করে লম্বা or-শৃঙ্খলের বদলে: role == "admin" or role == "editor" or role == "owner" না লিখে role in ("admin", "editor", "owner"). পড়তে সহজ, ভুলের সুযোগও কম। আর উল্টো প্রশ্নের জন্য not in লিখুন — "x" not in name — not "x" in name নয়।

গতির একটি পূর্বাভাস: list-এ in একটি একটি করে উপাদান মিলিয়ে দেখে, তাই দশ লাখ উপাদানে ধীর। Dict আর set-এ in hash ব্যবহার করে প্রায় সঙ্গে সঙ্গে উত্তর দেয়। কেন — তা দেখবেন পর্ব ১৬ ও ১৭-এ।

৬ · Assignment অপারেটর আর walrus :=

+=, -= আর তাদের পরিবার

score = score + 10 এত বেশি লেখা হয় যে এর একটি ছোট রূপ আছে: score += 10. প্রায় প্রতিটি গাণিতিক ও bitwise অপারেটরের এমন augmented assignment রূপ আছে — -=, *=, /=, //=, %=, **=, &=, |= ইত্যাদি। দুটি বিষয় জেনে রাখুন:

  • Python-এ ++ নেই। x++ লিখলে SyntaxError. আরও অদ্ভুত — ++x লিখলে কোনো error নেই, কিন্তু কিছুই বাড়ে না! Python এটিকে পড়ে +(+x), অর্থাৎ “x-এর ধনাত্মক রূপের ধনাত্মক রূপ” — মান একই থাকে। সবসময় x += 1 লিখুন।
  • Mutable object-এ += জায়গাতেই বদলায়। n += 1 একটি নতুন int বানিয়ে লেবেল সরায়। কিন্তু list-এ items += [5] পুরোনো list-এর ভেতরেই যোগ করে — তাই সেই list-এর অন্য কোনো নাম (alias) থাকলে সে-ও বদল দেখবে। এর পুরো আলোচনা পর্ব ১৯-এ।

Walrus := — হিসাবের মাঝখানেই নাম দেওয়া

সাধারণ = একটি statement — এটি কোনো মান ফেরত দেয় না, তাই if-এর শর্তের ভেতরে বসানো যায় না। কিন্তু মাঝে মাঝে একটি মান একবার হিসাব করে, সেটি দিয়ে শর্তও দেখতে চান, পরে ব্যবহারও করতে চান। এজন্য Python 3.8-এ এসেছে assignment expression :=, যাকে ডাকনামে বলা হয় walrus operator — কারণ := পাশ থেকে দেখতে সিন্ধুঘোটকের (walrus) দুই চোখ আর দুই দাঁতের মতো।

# walrus ছাড়া: len() দুবার, অথবা আলাদা লাইন
n = len(data)
if n > 3:
    print(f"{n}টি সংখ্যা")

# walrus দিয়ে: হিসাব, নাম দেওয়া আর পরীক্ষা — এক জায়গায়
if (n := len(data)) > 3:
    print(f"{n}টি সংখ্যা")

এর সবচেয়ে স্বাভাবিক ব্যবহার — খালি লাইন না আসা পর্যন্ত ইনপুট পড়া। ধরুন একটি চায়ের দোকানের দিনের বিক্রি এক এক করে লেখা হচ্ছে, খালি Enter মানে শেষ। while লুপ বিস্তারিত পর্ব ১২-এ; এখানে শুধু walrus-এর কাজটি দেখুন (ইনপুট: 50, 30, তারপর খালি লাইন):

1total = 02while (line := input()) != "":3    total += int(line)4print("মোট:", total)
ভ্যারিয়েবল
line
total
শর্ত
  1. শুরু। total নামটি 0-তে লাগল।
  2. প্রথম ঘোরা। input() পড়ল "50"; := সেটিকে line নাম দিল এবং একই মান expression-এর ফল হিসেবে দিল। তারপর "50" != "" — সত্য, তাই লুপের ভেতরে ঢোকা।
  3. যোগ। int("50") → 50, total এখন 50.
  4. দ্বিতীয় ঘোরা। আবার শর্ত — নতুন ইনপুট "30", নাম line, শর্ত সত্য।
  5. যোগ। total এখন 80.
  6. তৃতীয় ঘোরা। খালি লাইন: line হলো "", আর "" != "" মিথ্যা — লুপ শেষ। Walrus ছাড়া একই কাজে হয় input() দুই জায়গায় লিখতে হতো, নয়তো while True আর break লাগত।
  7. ফল। লুপের পরের লাইন ছাপে মোট: 80.

কয়েকটি নিয়ম মনে রাখুন। প্রথমত, walrus-এর চারপাশে সাধারণত বন্ধনী লাগে: (n := len(data)) > 3. বন্ধনী না দিলে n := len(data) > 3 মানে দাঁড়ায় n := (len(data) > 3) — n হয়ে যায় True! দ্বিতীয়ত, একা একটি লাইনে x := 5 লেখা যায় না — SyntaxError; সাধারণ assignment-এর জন্য =-ই নিয়ম।

কখন walrus ব্যবহার করবেন না: যখন এটি কোড ছোট করে কিন্তু পড়া কঠিন করে। একটি লাইনে একাধিক :=, শর্তের ভেতরে লুকানো গুরুত্বপূর্ণ assignment, বা শুধু “চালাক দেখানো”র জন্য ব্যবহার — এগুলো এড়িয়ে চলুন। PEP 572-এর নিজের পরামর্শও প্রায় এমন: যেখানে এটি স্পষ্টভাবে পড়া সহজ করে, শুধু সেখানেই। সন্দেহ হলে আলাদা লাইনে = লিখুন।

৭ · Bitwise অপারেটর — বিট ধরে কাজ, আর and বনাম &

পর্ব ০৭-এ দেখেছেন, কম্পিউটারের ভেতরে প্রতিটি int আসলে বিটের সারি — 6 মানে 110, 3 মানে 011. Bitwise operator-রা দুটি সংখ্যার বিটগুলো পাশাপাশি রেখে প্রতিটি ঘরে আলাদা কাজ করে:

অপারেটরনামপ্রতিটি বিটে নিয়মউদাহরণ
&ANDদুটোই 1 হলে 16 & 3 → 110 & 011 = 010 → 2
|ORযেকোনো একটি 1 হলে 16 | 3 → 111 → 7
^XORদুটি আলাদা হলে 16 ^ 3 → 101 → 5
~NOTসব বিট উল্টে দাও~6 → −7 (সবসময় -(x+1))
<<বামে সরাওপ্রতি ঘর সরানো মানে ×21 << 3 → 8
>>ডানে সরাওপ্রতি ঘর সরানো মানে ÷2 (নিচে গোল)16 >> 2 → 4

বাস্তব ব্যবহার — অনুমতির পতাকা (permission flags)

Linux-এ ফাইলের অনুমতি (read, write, execute) প্রতিটি একটি বিট: read = 4 (100), write = 2 (010), execute = 1 (001). একটি সংখ্যার ভেতরে তিনটি হ্যাঁ/না প্রশ্নের উত্তর রাখা যায় — একে বলে bitmask. chmod 755-এর 7 মানে 4+2+1, অর্থাৎ তিনটি অনুমতিই। কাজের নিয়ম:

  • অনুমতি যোগ করা: perm = READ | WRITE → 6
  • অনুমতি আছে কি না দেখা: perm & WRITE — শূন্য না হলে আছে
  • অনুমতি সরানো: perm & ~WRITE — শুধু ওই বিটটি 0 করা

and আর & এক নয়

C বা JavaScript থেকে আসা মানুষ, আর অনেক সময় AI-ও, যুক্তির জায়গায় & লিখে ফেলে। দুটির পার্থক্য তিনটি:

  1. and short-circuit করে; & সবসময় দুই দিকই হিসাব করে। তাই & দিয়ে “পাহারাদার শর্ত” কাজ করে না — বাম দিক মিথ্যা হলেও ডান দিকের ভাগ বা ফাংশন ডাক তবুও চলে।
  2. and truthiness দেখে; & বিট দেখে। 2 and 1 → 1 (truthy), কিন্তু 2 & 1 → 0 (falsy)! দুটি “সত্য” সংখ্যার & মিথ্যা হতে পারে।
  3. অগ্রাধিকার আলাদা। & তুলনার চেয়ে আগে হিসাব হয়, তাই a > 0 & b > 0 আসলে a > (0 & b) > 0 — একটি chained comparison! a = 5, b = 3 হলে এটি False দেয়, অথচ a > 0 and b > 0 দেয় True.

একটি ব্যতিক্রম জেনে রাখুন: pandas বা NumPy-তে পুরো কলাম ধরে শর্ত দিতে & আর |-ই ব্যবহার হয়, আর সেখানে প্রতিটি তুলনা বন্ধনীতে রাখতে হয় — (df.age > 18) & (df.city == "ঢাকা"). সেটি পর্ব ৪৬-এ। সাধারণ Python শর্তে সবসময় and/or.

এবার identity, membership, walrus আর bitwise — সব একসাথে চালিয়ে দেখুন:

a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b, a is b, a is c)

result = None
print(result is None)
print(int("100") is int("100"), int("1000") is int("1000"))

marks = {"রহিম": 80, "করিম": 72}
print("রহিম" in marks, 80 in marks, "ঢাকা" in "ঢাকা বিভাগ")

data = [3, 7, 1, 9, 4]
if (n := len(data)) > 3:
    print(f"{n}টি সংখ্যা — তালিকা লম্বা")

READ, WRITE, EXEC = 4, 2, 1
perm = READ | WRITE
print(perm, bool(perm & WRITE), bool(perm & EXEC))
print(6 & 3, 6 | 3, 6 ^ 3, ~6, 1 << 3, 16 >> 2)

প্রথম লাইন True False True — a আর b সমান কিন্তু আলাদা object, c হলো a-রই আরেক নাম। result is None হলো True. তৃতীয় লাইন True False দেখায় CPython-এর small int cache: 100 cache-এ আছে, 1000 নেই — ঠিক এজন্যই সংখ্যায় is ব্যবহার করা যায় না (এই ফল CPython-এর, ভাষার নিয়ম নয়)। "রহিম" in marks সত্য, কিন্তু 80 in marks মিথ্যা — dict-এ in শুধু key খোঁজে। Walrus একবারে len হিসাব করে n-এ রেখেছে, তাই ছাপা হলো “5টি সংখ্যা — তালিকা লম্বা”। অনুমতির লাইনে 6 True False — read আর write আছে, execute নেই। শেষ লাইন 2 7 5 -7 8 4 — উপরের টেবিলের সাথে মিলিয়ে নিন।

৮ · কে আগে? অগ্রাধিকারের নিয়ম আর a if cond else b

স্কুলে শিখেছেন “ভাগ-গুণ আগে, যোগ-বিয়োগ পরে”। Python-এর সব অপারেটরেরও তেমন একটি ক্রম আছে — operator precedence. নিচের টেবিলে ওপরের সারি আগে হিসাব হয় (সরল রূপ):

ক্রমঅপারেটরমনে রাখার কথা
১ (সবার আগে)( )বন্ধনী সব নিয়মের ওপরে
২**-2 ** 2 → −4; ডান থেকে বামে: 2 ** 3 ** 2 = 2 ** 9
৩-x, +x, ~xএকক চিহ্ন
৪* / // %গুণ-ভাগ
৫+ -যোগ-বিয়োগ
৬<< >>, তারপর &, ^, |bitwise — তুলনার আগে
৭== != < > <= >= is inসব তুলনা একই স্তরে, তাই chaining হয়
৮notnot a == b মানে not (a == b)
৯andor-এর আগে
১০or
১১a if c else bconditional expression
১২ (সবার শেষে):=তাই walrus-এ বন্ধনী লাগে

এই টেবিল মুখস্থ করার দরকার নেই। পেশাদাররাও পুরোটা মনে রাখেন না। তাঁরা মেনে চলেন একটি সোনালি নিয়ম: সন্দেহ হলেই বন্ধনী দিন। (a and b) or c লিখলে পাঠককে (আর ছয় মাস পরের নিজেকে) আর মাথা খাটাতে হয় না। বন্ধনীর কোনো গতির খরচ নেই।

সবচেয়ে পরিচিত বাগ: x == 1 or 2

মুখের ভাষায় আমরা বলি “ভূমিকা admin বা editor হলে…”, আর সেটি হুবহু লিখে ফেলি: if role == "admin" or "editor":. অগ্রাধিকার অনুযায়ী Python পড়ে (role == "admin") or "editor". ধরুন role হলো "guest" — প্রথম অংশ False, তাই or ফেরত দেয় "editor", যা একটি খালি-নয় string, অর্থাৎ truthy. ফলে যে কেউ admin-এর ঘরে ঢুকে যাবে! সঠিক রূপ: role == "admin" or role == "editor", অথবা আরও ভালো role in ("admin", "editor").

Conditional expression — এক লাইনে “এটা না হলে ওটা”

শেষ অপারেটরটি তিন অংশের: A if শর্ত else B. শর্ত সত্য হলে মান A, না হলে B. একে বলে conditional expression (অন্য ভাষায় “ternary operator”):

price = 30 if age < 18 or age >= 60 else 60
label = "পাস" if marks >= 33 else "ফেল"

এটিও একটি expression, তাই একটি মান হয়ে যায় — সরাসরি নামে দেওয়া বা f-string-এর ভেতরে বসানো যায়। তবে শর্ত জটিল হলে বা একাধিক শাখা থাকলে সাধারণ if/elif/else অনেক পরিষ্কার — সেটিই পর্ব ১১-এর বিষয়।

C/C++ জানলে

C-র &&, ||, !-এর জায়গায় Python-এ শব্দ and, or, not; আর এরা true/false না দিয়ে একটি operand ফেরত দেয়। C-তে 1 < x < 10 মানে (1 < x) < 10, Python-এ গণিতের মতো chaining. অগ্রাধিকারেও পার্থক্য: C-তে x & 1 == 0 মানে x & (1 == 0), কিন্তু Python-এ (x & 1) == 0. আর Python-এ ++/-- নেই, ternary-র রূপ a if c else b. বিস্তারিত তুলনার জন্য C কোর্সের অপারেটর পর্ব দেখুন।

পরীক্ষা ও ইন্টারভিউতে
  • is আর ==-এর পার্থক্য কী? — == মান তুলনা করে, is দেখে দুটি নাম একই object-এ কি না; is শুধু None-এর জন্য।
  • print(0 or "" or "x") আর print(3 and 0)-এর আউটপুট? — x আর 0; or প্রথম truthy, and প্রথম falsy মান ফেরত দেয়।
  • Short-circuit evaluation কী? — উত্তর নিশ্চিত হলেই বাকি operand হিসাব না করা; x != 0 and y / x-এ ভাগ-শূন্য এড়ায়।
  • Walrus operator কী, কোন version থেকে? — :=, একটি expression-এর ভেতরে নাম দেয় ও মানটি ফেরত দেয়; Python 3.8 (PEP 572) থেকে।
  • "10" < "9" কেন True? — String তুলনা অক্ষর ধরে ধরে, code point দিয়ে; প্রথম অক্ষর '1' < '9'.

৯ · AI-এর যুগে — অপারেটরের ছোট ভুল, বড় বাগ

অপারেটরের ভুল কোডে খুব ছোট দেখায় — একটি চিহ্ন, একটি শব্দ। তাই AI-এর লেখা কোড পড়ার সময় চোখ এড়িয়ে যায়। অথচ ঠিক এই ভুলগুলোই নিরাপত্তার ফাঁক (role == "admin" or "editor") বা মাঝে মাঝে ভুল ফলের মতো ধরা-কঠিন বাগ তৈরি করে। AI সাধারণত পুরোনো কোড, অন্য ভাষার অভ্যাস আর নতুনদের ফোরাম থেকে শিখেছে — তাই নিচের ভুলগুলো এখনো দেখা যায়:

AI প্রায়ই যা লেখেসমস্যাসঠিক রূপ
if x == None:PEP 8-বিরোধী; কাস্টম == বা NumPy-তে ভুল ফলif x is None:
if status is "ok":Interning-এর ওপর নির্ভর; SyntaxWarning; ইনপুটের string-এ মিথ্যা হতে পারেif status == "ok":
if a > 0 & b > 0:অগ্রাধিকার ভুল, short-circuit নেইif a > 0 and b > 0:
if x > 1 and x < 10:ভুল নয়, তবে অপ্রয়োজনে লম্বাif 1 < x < 10:
timeout = arg or 30arg = 0 বৈধ হলে চুপচাপ 30 হয়ে যায়30 if arg is None else arg
এক লাইনে একাধিক :=চালাক কিন্তু পড়া ও ডিবাগ করা কঠিনআলাদা লাইনে =

AI দিয়ে শর্ত-ভরা কোড (লগইন, ছাড়, অনুমতি) লেখালে বা যাচাই করালে নিচের কাঠামোটি কপি করে ব্যবহার করুন:

আমি Python 3.12 বা তার নতুন version ব্যবহার করি। নিচের কোডের প্রতিটি শর্ত (if, while, and/or, তুলনা) পরীক্ষা করো। শর্ত: ১. None-এর সাথে তুলনা সবসময় "is None" / "is not None" দিয়ে হতে হবে; অন্য কোথাও is থাকলে কারণ দেখাও। ২. and/or-এর জায়গায় & বা | থাকলে, আর "x == a or b" ধরনের ভুল থাকলে ধরিয়ে দাও। ৩. "x or ডিফল্ট" রীতি যেখানে আছে, সেখানে 0, "" বা [] বৈধ মান হতে পারে কি না বলো। ৪. প্রতিটি জটিল শর্তে অগ্রাধিকার অনুযায়ী বন্ধনী বসিয়ে দেখাও Python আসলে কীভাবে পড়ছে। ৫. walrus (:=) থাকলে বলো সেটি পড়া সহজ করছে, নাকি শুধু লাইন কমাচ্ছে। ৬. প্রতিটি সংশোধনের জন্য একটি ছোট উদাহরণ ইনপুট দাও, যেখানে পুরোনো কোড ভুল ফল দেয়। কোড: [এখানে কোড বসাও]

AI-এর লেখা শর্ত পেলে — যাচাইয়ের চেকলিস্ট

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

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে কোড-লেখক থেকে সিস্টেম-চিন্তক বানাবে।

প্র ০১দুটি অক্ষরের একটি অপারেটর := নিয়ে এত বিতর্ক হলো যে Python-এর স্রষ্টা নেতৃত্ব ছেড়ে দিলেন — ঘটনাটি কী, আর এর শিক্ষা কী?

২০১৮ সালের শুরুতে Chris Angelico একটি প্রস্তাব লেখেন — PEP 572, “Assignment Expressions”. ধারণা সরল: expression-এর ভেতরে নাম দেওয়ার উপায়, যাতে একই হিসাব দুবার করতে না হয়, বা while True আর break-এর ঘোরানো রূপ এড়ানো যায়। পরে Tim Peters আর Guido van Rossum নিজেও প্রস্তাবটির সহ-লেখক হন।

কিন্তু প্রস্তাবটি Python সম্প্রদায়ের ইতিহাসের সবচেয়ে উত্তপ্ত আলোচনাগুলোর একটি তৈরি করে। python-ideas আর python-dev মেইলিং লিস্টে কয়েক মাস ধরে শত শত বার্তা চলে। আপত্তি ছিল কয়েক ধরনের:

  • “একটি কাজ করার একটিই স্পষ্ট উপায়” — Zen of Python-এর এই নীতির সাথে দ্বন্দ্ব; এখন assignment-এর দুটি রূপ।
  • C-র পুরোনো ভুলের ভয় — C-তে if (x = 5) লেখা যায়, যা বহু বাগের উৎস। Python এতদিন ইচ্ছা করেই assignment-কে statement রেখেছিল।
  • পড়ার সহজতা — অনেকের মতে এটি Python-কে “চালাক কিন্তু ঘোলাটে” কোডের দিকে ঠেলে দেবে।

Guido তখন ছিলেন BDFL — “Benevolent Dictator For Life”, অর্থাৎ শেষ সিদ্ধান্তের মালিক। ২০১৮ সালের জুলাইয়ে তিনি PEP 572 গ্রহণ করেন। এর পরপরই, ১২ জুলাই, python-committers লিস্টে “Transfer of Power” শিরোনামে লেখেন যে তিনি BDFL-এর ভূমিকা থেকে স্থায়ীভাবে সরে যাচ্ছেন। তাঁর কথার মর্ম ছিল — একটি PEP-এর জন্য এত লড়াই করতে হলে, আর এত মানুষ তাঁর সিদ্ধান্ত অপছন্দ করছেন দেখলে, তিনি আর এই দায়িত্ব চান না। নতুন শাসনব্যবস্থা কেমন হবে, তা তিনি core developer-দের ওপরই ছেড়ে দেন।

এরপর কয়েক মাস ধরে একাধিক শাসন-মডেল (PEP 8000 সিরিজ) নিয়ে আলোচনা আর ভোট হয়। ২০১৮ সালের ডিসেম্বরে PEP 8016-এর মডেল জেতে — পাঁচ সদস্যের একটি নির্বাচিত Steering Council. ২০১৯ সালের শুরুতে প্রথম কাউন্সিল নির্বাচিত হয়, আর Guido নিজেও তার সদস্য ছিলেন। Walrus অপারেটর অবশেষে আসে Python 3.8-এ, ২০১৯ সালের অক্টোবরে। আজ এটি শান্তভাবে ব্যবহৃত হয় — বিশেষ করে while লুপ, regex match আর comprehension-এ — আর বেশিরভাগ স্টাইল গাইড এর সংযত ব্যবহারের পরামর্শ দেয়।

মূল উপলব্ধি: প্রোগ্রামিং ভাষা শুধু প্রযুক্তি নয়, একটি সম্প্রদায়ও। একটি ছোট syntax-এর সিদ্ধান্তও লাখো মানুষের দৈনন্দিন কোডকে প্রভাবিত করে, তাই ভাষার নকশায় প্রযুক্তিগত যুক্তির পাশাপাশি মানুষের ঐকমত্য আর সিদ্ধান্ত নেওয়ার প্রক্রিয়াও সমান গুরুত্বপূর্ণ।

প্র ০২বেশিরভাগ জনপ্রিয় ভাষায় 1 < x < 10 গণিতের মতো কাজ করে না, Python-এ করে। কেন Python এই পথ নিল, আর chaining-এর নিজস্ব কোনো ফাঁদ আছে কি?

C-পরিবারের ভাষায় (C, C++, Java, JavaScript) তুলনা একটি সাধারণ binary operator: বাম আর ডান নিয়ে একটি ফল দেয়। তাই 1 < x < 10 স্বাভাবিকভাবেই হয় (1 < x) < 10. এতে ভাষার নিয়ম সরল থাকে, কিন্তু মানুষের প্রত্যাশার সাথে মেলে না — গণিতের খাতায় সবাই a < b < c লিখে অভ্যস্ত।

Python-এর অফিসিয়াল Language Reference স্পষ্ট বলে: তুলনাগুলো যত খুশি জোড়া যায়, x < y <= z মানে x < y and y <= z, তবে y একবারই হিসাব হয়। এটি Python-এর একটি পুরোনো বৈশিষ্ট্য। এর পেছনে ভাবনাটি Python-এর সামগ্রিক দর্শনের সাথে মেলে — কোড যেন মানুষের পড়ার মতো হয়, যন্ত্রের সুবিধার মতো নয়। Python একা এমন নয়; যেমন Julia-তেও তুলনার chaining গণিতের মতো কাজ করে। (Python-এর এই নকশার উৎস ঠিক কোন ভাষা, তা নিয়ে নিশ্চিত দাবি করা কঠিন; গণিতের রীতিই মূল অনুপ্রেরণা বলে ধরা যায়।)

বাস্তবায়নের দিক: parser একটি chained তুলনাকে একটি একক “Compare” নোড হিসেবে রাখে, যাতে অপারেটরের একটি তালিকা আর operand-এর একটি তালিকা থাকে। Bytecode-এ মাঝের মান একবার হিসাব করে stack-এ কপি রাখা হয়, আর মিথ্যা পেলেই বাকিটা লাফিয়ে যায় — অর্থাৎ short-circuit.

কিন্তু ফাঁদও আছে:

  • a != b != c মানে “তিনটিই আলাদা” নয় — এর মানে শুধু a != b and b != c. তাই 1 != 2 != 1 সত্য, অথচ প্রথম আর শেষটি সমান।
  • is আর in-ও তুলনা-পরিবারের সদস্য, তাই এরাও chain হয়: "a" in "abc" == True আসলে ("a" in "abc") and ("abc" == True) — ফল False! (তাছাড়া == True লেখাই অপ্রয়োজনীয়।)
  • a < b > c বৈধ, কিন্তু পড়তে বিভ্রান্তিকর; chaining-এ সব তুলনা একই দিকে রাখাই ভালো রীতি।

মূল উপলব্ধি: ভাষার নকশায় “যন্ত্রের জন্য সরল নিয়ম” আর “মানুষের জন্য স্বাভাবিক অর্থ” প্রায়ই মুখোমুখি দাঁড়ায়। Python সাধারণত মানুষের পক্ষ নেয় — কিন্তু তাতেও নতুন কিছু কোণা তৈরি হয়, যা জানা থাকলেই নিরাপদ।

প্র ০৩Short-circuit evaluation-এর ধারণা কোথা থেকে এলো? সব ভাষা কি and/or-এ একইভাবে থামে?

Short-circuit-কে কখনো কখনো “McCarthy evaluation” বলা হয়, Lisp-এর স্রষ্টা John McCarthy-র নামে। ১৯৬০ সালে প্রকাশিত Lisp-এর বিখ্যাত প্রবন্ধে McCarthy “conditional expression” ধারণাটি ব্যবহার করেন — “যদি p, তাহলে a; নইলে b” — আর যুক্তির “এবং”, “অথবা”-কে সংজ্ঞায়িত করেন এই conditional expression দিয়েই। “p এবং q” মানে “যদি p সত্য হয় তবে q-এর মান, নইলে মিথ্যা” — এই সংজ্ঞায় p মিথ্যা হলে q-কে দেখার প্রশ্নই আসে না। অর্থাৎ short-circuit এখানে কোনো অপ্টিমাইজেশন নয়, অর্থেরই অংশ।

অন্য ভাষাগুলো ভিন্ন ভিন্ন পথ নিয়েছে:

  • C: Dennis Ritchie তাঁর C-র ইতিহাসের লেখায় বর্ণনা করেছেন, পূর্বসূরি B আর BCPL-এ & আর | শর্তের ভেতরে থাকলে short-circuit করত, অন্য জায়গায় bitwise কাজ করত। এই দ্বৈত অর্থ দূর করতে C আলাদা && আর || যোগ করে, যারা সবসময় short-circuit করে। কিন্তু অগ্রাধিকারের পুরোনো ক্রম রয়ে যায় — এজন্যই C-তে x & 1 == 0-এর ফাঁদ।
  • Pascal: মূল মানদণ্ড ঠিক করে দেয়নি দুই দিক হিসাব হবে কি না; compiler-ভেদে আচরণ আলাদা ছিল, তাই নিরাপদ কোড লিখতে প্রোগ্রামারদের শর্ত ভেঙে লিখতে হতো।
  • Ada: দুটোই দেয় — সাধারণ and (দুই দিক হিসাব হতে পারে) আর স্পষ্ট and then / or else (নিশ্চিত short-circuit).
  • Python: and/or সবসময় short-circuit — এটি ভাষার নিয়মে লেখা প্রতিশ্রুতি। আর bitwise &/| আলাদা অপারেটর, যারা কখনো short-circuit করে না।

বাস্তবে এই নিশ্চয়তা খুব দামি। user is not None and user.is_active, items and items[0], n != 0 and total / n — এগুলো “পাহারাদার শর্ত” (guard) হিসেবে কাজ করে, কারণ ডান দিকটি কেবল তখনই চলে যখন সেটি চালানো নিরাপদ। আরেকটি ব্যবহারিক কৌশল: সস্তা পরীক্ষা বাম দিকে, ব্যয়বহুল পরীক্ষা (ডেটাবেস বা নেটওয়ার্ক) ডান দিকে রাখলে অনেক সময় ব্যয়বহুল কাজটি এড়ানো যায়।

মূল উপলব্ধি: একটি ছোট অর্থগত সিদ্ধান্ত — “দ্বিতীয় অংশ কখন হিসাব হবে” — নিরাপদ কোড লেখার ভিত্তি হয়ে উঠতে পারে। যে ভাষা এটি নিশ্চিত করে, সেখানে আপনি শর্তকেই নিয়ন্ত্রণের হাতিয়ার হিসেবে ব্যবহার করতে পারেন।

প্র ০৪x is 5 একটি বৈধ Python কোড, তবু Python 3.8 থেকে compiler সতর্ক করে। বৈধ কোডে warning কেন, আর error-ই বা কেন নয়?

is-এর অর্থ পরিষ্কার: দুটি object একই কি না। তাই x is 5 ব্যাকরণগতভাবে ঠিক, আর এর একটি নির্দিষ্ট অর্থও আছে। সমস্যা হলো, সেই অর্থ প্রায় কখনোই প্রোগ্রামারের উদ্দেশ্য নয়। যিনি x is 5 লেখেন, তিনি আসলে জানতে চান “x-এর মান কি 5?” আর সেই প্রশ্নের উত্তর is দেয় কেবল ঘটনাচক্রে।

CPython-এ −5 থেকে 256 পর্যন্ত int আগে থেকে তৈরি থাকে, কিছু string intern করা হয়, আর একই কোড-অংশের সমান constant একটিমাত্র object-এ মিশে যায়। ফলে পরীক্ষার সময় x is 5 প্রায়ই সত্য দেখায় — প্রোগ্রামার ভাবেন কোড ঠিক আছে। পরে বড় সংখ্যা, ইনপুট থেকে আসা string, বা অন্য implementation (যেমন PyPy) — হঠাৎ ভুল ফল। এমন বাগ খুব বিপজ্জনক, কারণ এটি নিয়মিত নয়, মাঝে মাঝে ঘটে।

বহু বছর ধরে এটি শুধু স্টাইল গাইড আর অভিজ্ঞদের সতর্কবাণীতে ছিল। Python 3.8-এ compiler নিজেই এমন লেখা ধরতে শুরু করে: is বা is not-এর এক পাশে সংখ্যা, string, tuple-এর মতো literal থাকলে SyntaxWarning. পরের version-গুলোতে বার্তাটি আরও নির্দিষ্ট হয়েছে — 3.14-এ এটি বলে "is" with 'int' literal. Did you mean "=="?

তাহলে error কেন নয়? কয়েকটি কারণে। প্রথমত, পুরোনো কোড ভাঙা Python-এর নীতিবিরুদ্ধ; লাখো লাইনের পুরোনো কোডে এমন লেখা থাকতে পারে, যা “কাজ করছে”। দ্বিতীয়ত, ভাষার নিয়মে এর অর্থ সংজ্ঞায়িত — একে অবৈধ বলার যুক্তিগত ভিত্তি দুর্বল। Python-এর ঐতিহ্য হলো এমন ক্ষেত্রে প্রথমে warning, প্রয়োজনে পরে কঠোরতা। (একই পথে 3.12-এ ভুল escape sequence, যেমন "\d", DeprecationWarning থেকে SyntaxWarning হয়েছে।)

একটি সূক্ষ্ম বিষয়: এই warning আসে compile-এর সময়, চলার সময় নয়। পর্ব ০৫-এ দেখেছেন, import করা module-এর bytecode .pyc-এ জমা থাকে। তাই প্রথমবার warning দেখা গেলেও, পরের বার .pyc থেকে চালালে আর দেখা নাও যেতে পারে — warning দেখলে তখনই ঠিক করুন।

মূল উপলব্ধি: ভালো টুল শুধু “অবৈধ” কোড আটকায় না, “বৈধ কিন্তু প্রায় নিশ্চিত ভুল” কোডও চিহ্নিত করে। Warning-কে আওয়াজ মনে না করে compiler-এর দেওয়া বিনামূল্যের কোড রিভিউ হিসেবে দেখুন।

প্র ০৫Python-এর and/or কেন True/False না দিয়ে operand ফেরত দেয়? এই নকশার সুবিধা আর ফাঁদ কী?

এই নকশা Python একা বেছে নেয়নি। Lisp-এ (or nil 5) ফেরত দেয় 5 — Lisp-এ “মিথ্যা” মানে শুধু nil, বাকি সব সত্য, তাই প্রথম “সত্য” মানটিই ফল হিসেবে ফেরত দেওয়া স্বাভাবিক। Perl, Ruby আর JavaScript-এর ||-ও একইভাবে operand ফেরত দেয়; JavaScript-এ name || "Guest" বহুদিনের পরিচিত রীতি। যুক্তিটি সরল: যেহেতু truthiness আছে, operand নিজেই যথেষ্ট “সত্য” বা “মিথ্যা”; তাকে আলাদা করে True-তে বদলালে তথ্য হারায়, অথচ লাভ নেই।

সুবিধা: ডিফল্ট মান (name or "অতিথি"), প্রথম পাওয়া মান বাছাই (a or b or c), আর পাহারাদার শর্ত (user and user.name) — সবই এক লাইনে।

ইতিহাসের একটি শিক্ষা: Python 2.5-এর আগে ভাষায় কোনো conditional expression ছিল না। মানুষ তখন লিখত cond and x or y — “cond সত্য হলে x, নইলে y”. কিন্তু এই কৌশল ভেঙে যায় যখন x নিজেই falsy: True and 0 or 5 দেয় 5, 0 নয়! এই বহুল-প্রচলিত বাগের কারণেই PEP 308 গৃহীত হয়, আর Python 2.5 (২০০৬)-এ আসে x if cond else y. Syntax বাছাই নিয়েও তখন দীর্ঘ বিতর্ক আর সম্প্রদায়ের ভোট হয়েছিল; শেষে Guido বর্তমান রূপটি বেছে নেন।

আজকের ফাঁদ: or দেখে truthiness, “মান আছে কি না” নয়। port = config_port or 8080 — কেউ ইচ্ছা করে 0 দিলে তা হারিয়ে যায়। JavaScript এই সমস্যার জন্য ২০২০ সালে (ES2020) আলাদা অপারেটর ?? যোগ করেছে, যা কেবল null/undefined হলে ডিফল্ট দেয়। Python-এ একই ধরনের প্রস্তাব আছে — PEP 505 (“None-aware operators”) — কিন্তু সেটি এখনো গৃহীত হয়নি, স্থগিত অবস্থায় আছে। তাই Python-এ স্পষ্ট লিখতে হয়: 8080 if config_port is None else config_port.

মূল উপলব্ধি: একটি নকশা একই সাথে সংক্ষিপ্ত ও শক্তিশালী হতে পারে, আবার সূক্ষ্ম বাগের উৎসও। Operand ফেরত দেওয়া and/or ব্যবহার করুন — তবে প্রতিবার জিজ্ঞেস করুন, “এখানে falsy কিন্তু বৈধ কোনো মান আসতে পারে কি?”

অনুশীলন

প্রথমে খাতায় উত্তর লিখুন, তারপর REPL-এ চালিয়ে মিলিয়ে নিন। অনুমান আর বাস্তবের পার্থক্যই সবচেয়ে বেশি শেখায়।

  1. আউটপুট অনুমান: প্রতিটির মান কী হবে? 3 < 5 < 4, "Zebra" < "apple", [] or "ফাঁকা", "চা" and 0, not 0 == 1, 2 ** 3 ** 2.

    3 < 5 < 4 → False (3 < 5 সত্য, কিন্তু 5 < 4 মিথ্যা)। "Zebra" < "apple" → True, কারণ বড় হাতের Z (90) ছোট হাতের a (97)-এর আগে। [] or "ফাঁকা" → "ফাঁকা" (প্রথম truthy). "চা" and 0 → 0 (প্রথম falsy). not 0 == 1 → True, কারণ এটি not (0 == 1). 2 ** 3 ** 2 → 512, কারণ ** ডান থেকে বামে: 2 ** 9.

  2. বাগ ধরুন: একটি দোকানের অ্যাপে লেখা আছে if payment == "bKash" or "Nagad": — উদ্দেশ্য, শুধু এই দুই মাধ্যমে ক্যাশব্যাক। কেউ “cash” দিলে কী হবে? দুইভাবে ঠিক করুন।

    শর্তটি পড়া হয় (payment == "bKash") or "Nagad". payment = "cash" হলে প্রথম অংশ মিথ্যা, তাই ফল "Nagad" — একটি খালি-নয় string, truthy. ফলে সবাই ক্যাশব্যাক পাবে! সমাধান ১: payment == "bKash" or payment == "Nagad". সমাধান ২ (ভালো): payment in ("bKash", "Nagad").

  3. অনুমতির হিসাব: READ, WRITE, EXEC = 4, 2, 1. একজন ব্যবহারকারীর অনুমতি perm = 7. (ক) write সরিয়ে দিন — নতুন মান কত? (খ) তারপর execute আছে কি না কীভাবে দেখবেন? (গ) perm and WRITE লিখলে কেন ভুল উত্তর আসতে পারে?

    (ক) perm = perm & ~WRITE (বা perm &= ~WRITE) → 111 & 101 = 101 → 5. (খ) perm & EXEC != 0 — Python-এ & তুলনার আগে হিসাব হয়, তাই এটি (perm & EXEC) != 0, ফল True; আরও পরিষ্কার bool(perm & EXEC). (গ) and বিট দেখে না, শুধু truthiness দেখে: perm শূন্য না হলেই ফল WRITE, অর্থাৎ 2 — write বিট আসলে না থাকলেও “সত্য” দেখাবে। যেমন perm = 5 হলে 5 and 2 → 2 (truthy), অথচ 5 & 2 → 0.

  4. AI অডিট: যেকোনো AI-কে বলুন: “Python-এ একটি ফাংশন লেখো, যা বয়স আর ছাত্র কার্ড আছে কি না নিয়ে টিকিটের দাম বলে; বয়স না দিলে None আসতে পারে।” ফলাফলে এই পর্বের চেকলিস্ট চালান — == None, is, &, or-ডিফল্ট আর সীমানার মান (ঠিক 18, ঠিক 60).

    প্রায়ই পাবেন if age >= 18 and age < 60 (ঠিক, তবে 18 <= age < 60 বেশি পরিষ্কার)। কখনো if age == None আসে — বদলে is None করুন। সবচেয়ে বেশি যা চোখ এড়ায়: সীমানা। “১৮ থেকে ৬০” মানে কি ৬০ অন্তর্ভুক্ত? AI নিজে একটি সিদ্ধান্ত নিয়ে নেয়, আর বলে না। ঠিক 18, 59, 60 আর None দিয়ে চালিয়ে দেখুন। আর যদি age = age or 18 জাতীয় কিছু থাকে, ভাবুন — শিশু বয়স 0 দিলে কী হবে? শিক্ষা: AI-এর শর্ত চলে ঠিকই, কিন্তু নিয়মের ব্যাখ্যা আপনাকেই যাচাই করতে হবে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমPython অপারেটরগুলো আগে থেকে পরে হিসাব হওয়ার ক্রমে সাজান (▲▼ চেপে সরান)

  1. ** — ঘাত
  2. * / // % — গুণ-ভাগ
  3. == < in is — তুলনা
  4. not
  5. and
  6. or

Python-এ print(5 and 0 and 7) কী ছাপবে?

  • False
  • 7
  • 0
  • TypeError

Python-এ 1 < f() < 10 কোনটির সমান (যেখানে f() একটি ফাংশন ডাক)?

  • (1 < f()) < 10
  • 1 < f() and f() < 10-এর মতো, তবে f() মাত্র একবার চলে
  • 1 < f() and f() < 10, আর f() দুবার চলে
  • এটি SyntaxError

একটি ভ্যারিয়েবল None কি না দেখার সবচেয়ে সঠিক Python রীতি কোনটি?

  • if x == None:
  • if not x:
  • if type(x) == type(None):
  • if x is None:

Python-এ "10" < "9"-এর মান কী, এবং কেন?

  • True — কারণ অক্ষর ধরে তুলনায় '1' আসে '9'-এর আগে
  • False — কারণ 10 বড় সংখ্যা
  • TypeError
  • False — কারণ "10" লম্বা string

Walrus operator := সম্পর্কে কোন কথাটি সঠিক?

  • এটি =-এর নতুন রূপ, তাই x := 5 একা এক লাইনে লেখাই এখন রীতি
  • এটি expression-এর ভেতরে একটি নামে মান রাখে আর সেই মানটি ফেরত দেয়; Python 3.8 থেকে আছে
  • এটি দুটি মান সমান কি না পরীক্ষা করে
  • এটি Python 3.12-এ সরিয়ে ফেলা হয়েছে

role = "guest" হলে Python-এ role == "admin" or "editor"-এর মান কী?

  • False
  • True
  • "editor"
  • SyntaxError

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. অপারেটর হলো প্রশ্ন, আর প্রতিটি অপারেটর-যুক্ত লেখা একটি expression — হিসাব শেষে একটি মান, প্রায়ই True/False.
  2. তুলনা chain হয়: 18 <= age < 60 মানে দুটি তুলনা and দিয়ে, মাঝের মান একবার। String তুলনা অক্ষর ধরে, code point দিয়ে — "a" < "B" মিথ্যা, "10" < "9" সত্য।
  3. and/or short-circuit করে আর একটি operand ফেরত দেয় — or প্রথম truthy, and প্রথম falsy. x or ডিফল্ট-এ 0-এর মতো বৈধ falsy মান হারাতে পারে।
  4. == মান তুলনা করে, is দেখে একই object কি না। is শুধু None-এর জন্য; সংখ্যা বা string-এ কখনো নয় (cache আর interning অবিশ্বস্ত)।
  5. in সংগ্রহে খোঁজে (dict-এ শুধু key); := হিসাবের মাঝে নাম দেয়, সংযতভাবে; & | বিটের জন্য, যুক্তির জন্য and or; সন্দেহ হলে বন্ধনী.

পরের পর্বে: আজ দেখলেন string-ও তুলনা করা যায়, in দিয়ে তার ভেতরে খোঁজা যায়। এবার string-কে পুরোপুরি চিনব — slicing, কাজের method-গুলো, আর বাংলা লেখা Python-এর ভেতরে কীভাবে থাকে।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান