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

ইনপুট-আউটপুট — input() আর f-string

Input & Output in Python — input(), Type Conversion and f-strings

Taught in Bangla, this lesson covers console input and output in Python: why input() always returns text, validating bad input, reading several values with split() and map(), f-strings and format specifiers, printing aligned tables, older formatting styles, stderr and flush.

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

  • stdin আর stdout কী, ইনপুট আসলে কোথা থেকে আসে — আর এই কোর্সের কোড-বক্সে ইনপুট কীভাবে আগে থেকে দেওয়া থাকে
  • input()-এর ফেরত মান, int()/float() দিয়ে রূপান্তর, আর ভুল ইনপুটে কী হয়
  • split() আর map() দিয়ে এক লাইন থেকে কয়েকটি সংখ্যা পড়া
  • f-string ও format spec mini-language: চওড়া, সারিবদ্ধতা, দশমিক, কমা, শতাংশ, শূন্য দিয়ে ভরা, {x=}
  • সাজানো টেবিল ছাপানো, বাংলা লেখায় কলাম বাঁকা হওয়ার কারণ, পুরোনো format()/% ধাঁচ, stderr আর flush

১ · প্রোগ্রাম মানে একটি কথোপকথন

bKash অ্যাপে টাকা পাঠানোর কথা ভাবুন। অ্যাপ জিজ্ঞেস করে, “কাকে পাঠাবেন?” আপনি নম্বর দেন। “কত টাকা?” আপনি অঙ্ক লেখেন। অ্যাপ হিসাব করে জানায়, “চার্জসহ মোট এত টাকা কাটবে।” পুরো ব্যাপারটি একটি কথোপকথন — প্রশ্ন, উত্তর, তারপর ফল। এখন পর্যন্ত আমাদের Python প্রোগ্রামগুলো ছিল একতরফা বক্তৃতার মতো: সব মান কোডের ভেতরেই লেখা, প্রোগ্রাম শুধু বলে যায়। এই পর্বে প্রোগ্রাম প্রথমবার শুনতে শিখবে।

প্রোগ্রামের ভেতরে যা ঢোকে তা input, আর যা বের হয় তা output. পর্ব ০৪-এ আপনি print() দিয়ে আউটপুটের দিকটা শিখেছেন — sep, end সহ। এবার দেখব উল্টো দিকটা, আর তারপর আউটপুটকে সুন্দর করে সাজানোর আধুনিক হাতিয়ার f-string.

ইনপুট আসে একটি “নল” দিয়ে — stdin

একটি প্রোগ্রাম চালু হলেই অপারেটিং সিস্টেম তাকে তিনটি তৈরি সংযোগ দিয়ে দেয়। এদের বলে stream — এক মাথা দিয়ে লেখা ঢোকে, অন্য মাথা দিয়ে বের হয়, অনেকটা পানির নলের মতো। প্রথমটি stdin (standard input), যা দিয়ে ইনপুট ঢোকে। দ্বিতীয়টি stdout (standard output), যেখানে print() লেখে। তৃতীয়টি stderr (standard error), যা error-বার্তার জন্য আলাদা রাখা — অংশ ৮-এ দেখবেন। সাধারণত stdin-এর মাথায় থাকে কীবোর্ড আর stdout-এর মাথায় থাকে টার্মিনালের পর্দা। কিন্তু এটা বাধ্যতামূলক নয়: stdin-এর মাথায় একটি ফাইল বা অন্য প্রোগ্রামও জোড়া যায়। প্রোগ্রাম নিজে টেরও পায় না লেখাটা কে পাঠাল।

  1. কীবোর্ড বা ফাইল25 ⏎উৎস
  2. stdin"25\n"অপেক্ষার সারি
  3. input()'25'সবসময় str
  4. আপনার কোডint('25') + 1রূপান্তর ও হিসাব
  5. print() → stdout26পর্দায়
  1. উৎস। ব্যবহারকারী কীবোর্ডে 2, 5 চেপে Enter দিলেন। Enter-ও একটি অক্ষর — newline. টার্মিনাল সাধারণত Enter না চাপা পর্যন্ত লেখাটা প্রোগ্রামকে দেয় না, যাতে ব্যবহারকারী ভুল হলে মুছে ঠিক করতে পারেন।
  2. stdin — অপেক্ষার সারি। পুরো লাইন "25\n" stdin-এ জমা হলো। input() না ডাকা পর্যন্ত লেখাটা এখানেই অপেক্ষা করে। আর stdin-এর মাথায় কীবোর্ডের বদলে একটি ফাইল থাকলে, ফাইলের লাইনগুলোও ঠিক এভাবেই সারিতে দাঁড়ায়।
  3. input() একটি লাইন তুলে নেয়। সারি থেকে একটি লাইন নিয়ে শেষের newline কেটে ফেলে, আর ফেরত দেয় '25' — একটি string. সংখ্যা নয়! কীবোর্ড থেকে যা-ই আসুক, input()-এর চোখে সবই লেখা।
  4. রূপান্তর আপনার দায়িত্ব। সংখ্যা দরকার হলে int() দিয়ে নিজে বদলাতে হয়। এই ধাপটি ভুলে যাওয়াই নতুনদের (এবং AI-এরও) সবচেয়ে সাধারণ ভুল — অংশ ২-এ বিস্তারিত।
  5. ফল ফেরত যায় stdout দিয়ে। print() লেখাটি stdout-এ পাঠায়, সেখান থেকে পর্দায়। লক্ষ করুন, print(26) সংখ্যাটিকে আবার লেখায় ("26") বদলে পাঠায় — নলের ভেতর দিয়ে শুধু লেখাই চলে, দুই দিকেই।
মূল ধারণা

প্রোগ্রাম আর বাইরের জগতের মধ্যে আদান-প্রদান হয় শুধু লেখা দিয়ে। ঢোকার সময় লেখাকে সংখ্যায় বদলান (int, float), বের হওয়ার সময় সংখ্যাকে সুন্দর লেখায় বদলান (f-string). এই পর্বের বাকি সবকিছু এই দুই রূপান্তরের খুঁটিনাটি।

এই কোর্সের কোড-বক্সে ইনপুট কোথা থেকে আসে?

একটি জরুরি কথা, যা না জানলে এই পর্বের কোড চালিয়ে বিভ্রান্ত হবেন। এই সাইটের “চালান” বোতাম কোডটি একটি অনলাইন সার্ভারে পাঠায়, সেখানে Python চলে, আর ফল ফিরে আসে। সার্ভারে বসে কেউ আপনার কীবোর্ড থেকে টাইপ নিতে পারে না। তাই যে কোড-বক্সে input() আছে, তার ইনপুট আমরা আগে থেকেই লিখে রেখেছি (পেজের ভেতরে data-stdin নামের একটি জায়গায়)। প্রতিটি বক্সের নিচের ব্যাখ্যায় বলা থাকবে কোন ইনপুট দেওয়া হয়েছে।

এর একটি দৃশ্যমান ফল আছে। নিজের কম্পিউটারে চালালে আপনার টাইপ করা লেখা পর্দায় দেখা যায়, কারণ টার্মিনাল নিজেই সেটা দেখায় (echo). কিন্তু ইনপুট আগে থেকে দেওয়া থাকলে কেউ টাইপ করে না, তাই উত্তরগুলো আউটপুটে আসে না — শুধু প্রশ্নগুলো (prompt) পরপর একই লাইনে দেখা যায়। ঠিক একই জিনিস ঘটে টার্মিনালে ফাইল থেকে ইনপুট দিলে:

# answers.txt ফাইলের লাইনগুলো stdin-এ যাবে, কীবোর্ড লাগবে না
python bmi.py < answers.txt

# অথবা অন্য একটি কমান্ডের আউটপুট সরাসরি stdin-এ
echo "3 5" | python add.py

প্রোগ্রামিং কনটেস্ট আর অনলাইন জাজ (Codeforces, বিশ্ববিদ্যালয়ের প্রোগ্রামিং কনটেস্ট) ঠিক এভাবেই কাজ করে: বিচারক আপনার প্রোগ্রামের stdin-এ পরীক্ষার ডেটা ঢেলে দেয়, stdout থেকে উত্তর মিলিয়ে দেখে। তাই কনটেস্টের কোডে prompt লেখা হয় না — input("সংখ্যা দিন: ")-এর ওই লেখাটুকুও stdout-এ যায়, আর উত্তর না মেলার কারণ হতে পারে।

২ · input() — সবসময় লেখা ফেরত দেয়

input() একটি built-in function. বন্ধনীর ভেতরে একটি লেখা দিলে সেটি prompt হিসেবে পর্দায় ছাপা হয় — নতুন লাইন ছাড়া, যাতে ব্যবহারকারী ঠিক তার পাশেই টাইপ করেন। তারপর প্রোগ্রাম থেমে অপেক্ষা করে। Enter চাপা হলে পুরো লাইনটি (শেষের newline বাদে) একটি str হিসেবে ফেরত আসে। সাধারণত সেটিকে একটি variable-এ রাখা হয়:

name = input("আপনার নাম: ")
print("স্বাগতম,", name)

$ python hello.py
আপনার নাম: সুমাইয়া
স্বাগতম, সুমাইয়া

নামের ক্ষেত্রে কোনো সমস্যা নেই, কারণ নাম তো লেখাই। ঝামেলা শুরু হয় সংখ্যা চাইলে। পর্ব ০৬-এ দেখেছেন, প্রতিটি object-এর একটি type আছে, আর "25" (str) ও 25 (int) সম্পূর্ণ আলাদা জিনিস। পর্ব ০৯-এ দেখেছেন, দুটি str-এ + দিলে জোড়া লাগে। তাহলে নিচের প্রোগ্রামে কী হবে, অনুমান করুন:

a = input("প্রথম সংখ্যা: ")    # ব্যবহারকারী লিখলেন 25
b = input("দ্বিতীয় সংখ্যা: ")  # ব্যবহারকারী লিখলেন 5
print(a + b)
255
print(a * 3)
252525
print(a + 1)
TypeError: can only concatenate str (not "int") to str

30 নয়, 255! Python ভুল করেনি — দুটি লেখা পাশাপাশি জুড়েছে, ঠিক যেমন "ঢাকা" + "বাসী". আর লেখার সাথে সংখ্যা যোগ করতে বললে সে সোজা আপত্তি জানায়। এই কারণেই সংখ্যার ইনপুট নিলে প্রথম কাজ হলো type conversion — লেখাকে সংখ্যায় বদলানো। পূর্ণসংখ্যার জন্য int(), দশমিক সংখ্যার জন্য float(). নিচের ডায়াগ্রামে একটি বয়সের ইনপুটের পুরো জীবন দেখুন — তিন রকম ব্যবহারকারীর উত্তর দিয়ে:

1text = input("বয়স: ")2age = int(text)3print("আগামী বছর:", age + 1)
মেমরিতে কী আছে
stdin
text
age
  1. Prompt ছাপা হলো, প্রোগ্রাম থেমে আছে। “বয়স: ” লেখার পরে নতুন লাইন নেই, তাই কার্সর ঠিক পাশেই। ব্যবহারকারী 25 লিখে Enter দিলেন; stdin-এ জমল "25\n".
  2. input() ফেরত দিল '25'. newline কেটে বাদ, বাকিটা একটি str. এই মুহূর্তে text + 1 লিখলে TypeError হতো। type(text) দেখলে আসত <class 'str'>.
  3. int(text) — রূপান্তর। int() লেখাটি পড়ে দেখে এটি একটি বৈধ পূর্ণসংখ্যা কি না, তারপর নতুন একটি int object বানায়: 25. মূল text বদলায়নি — str বদলানো যায় না (পর্ব ০৯), রূপান্তর সবসময় নতুন object দেয়।
  4. এবার হিসাব কাজ করে। age + 1 = 26. লক্ষ করুন, int() লেখার দুই পাশের space আর newline নিজেই উপেক্ষা করে — int(" 25 ")-ও 25 দেয়।
  5. ব্যবহারকারী বাংলা অঙ্কে লিখলেন ২৫. অনেকেই অবাক হন: এটাও কাজ করে! int() শুধু 0–9 নয়, Unicode-এ “দশমিক অঙ্ক” হিসেবে চিহ্নিত যেকোনো লিপির অঙ্ক চেনে — বাংলা, দেবনাগরী, আরবি সব। int("২৫") দেয় 25, float("২৫.৫") দেয় 25.5. বাংলা কিবোর্ডের ব্যবহারকারীদের জন্য দারুণ সুবিধা।
  6. ব্যবহারকারী লিখলেন 25.5. এটা দশমিক সংখ্যা, পূর্ণসংখ্যা নয়, তাই int() রাজি নয় — প্রোগ্রাম ValueError দিয়ে থেমে গেল, age তৈরিই হলো না। মেসেজে Python ঠিক কোন লেখাটা বুঝতে পারেনি তা উদ্ধৃতিসহ দেখায়। দশমিক মান চাইলে আগে থেকেই float() ব্যবহার করুন।

রূপান্তরকে আরও সংক্ষেপে এক লাইনেও লেখা যায়, আর বাস্তবে সেটাই বেশি দেখবেন: age = int(input("বয়স: ")). ভেতরের ডাক আগে চলে (input দেয় str), বাইরের ডাক তারপর (int দেয় সংখ্যা)। পড়ার নিয়ম: ভেতর থেকে বাইরে।

ইনপুট লেখাint(...)float(...)মন্তব্য
"25"2525.0স্বাভাবিক ক্ষেত্র
" 25\n"2525.0দুই পাশের ফাঁকা জায়গা উপেক্ষিত
"২৫"2525.0বাংলা অঙ্কও চলে
"-7"-7-7.0চিহ্নসহ সংখ্যা চলে
"25.5"ValueError25.5দশমিকের জন্য float
"1,000"ValueErrorValueErrorকমা চলে না; আগে .replace(",", "")
"" (শুধু Enter)ValueErrorValueErrorখালি লেখা কোনো সংখ্যা নয়
"পঁচিশ"ValueErrorValueErrorকথায় লেখা সংখ্যা Python বোঝে না

দশমিক লেখা থেকে পূর্ণসংখ্যা চাইলে কেউ কেউ লেখেন int(float("25.7")) — এটি দেয় 25, 26 নয়। int() দশমিক অংশ কেটে ফেলে, পূর্ণসংখ্যায় গোল করে না। কাছের পূর্ণসংখ্যা চাইলে round(), আর তার নিজস্ব কিছু চমক আছে — পর্ব ০৭-এ দেখেছেন।

৩ · ভুল ইনপুট সামলানো — আর একটি পুরো প্রোগ্রাম

বাস্তবের ব্যবহারকারী আপনার নির্দেশ মানবেন না — এটা ধরে নিয়েই প্রোগ্রাম লিখতে হয়। কেউ বয়সের জায়গায় নাম লিখবেন, কেউ ভুলে শুধু Enter চাপবেন, কেউ 25 years লিখবেন। প্রতিবার প্রোগ্রাম লাল traceback দেখিয়ে মরে গেলে ব্যবহারকারী ভাববেন অ্যাপটাই খারাপ। ভুল ইনপুটে Python কী দেখায়:

বয়স: পঁচিশ
Traceback (most recent call last):
  File "age.py", line 2, in <module>
    age = int(text)
ValueError: invalid literal for int() with base 10: 'পঁচিশ'

পর্ব ০৪-এর নিয়মে শেষ লাইন আগে পড়ুন: ValueError মানে “type ঠিক আছে (str-ই দিয়েছেন), কিন্তু মানটা এই কাজের উপযুক্ত নয়।” base 10 মানে দশভিত্তিক সংখ্যা হিসেবে পড়ার চেষ্টা করা হয়েছিল। এটি একটি exception — আর exception ধরে সামলানোর পূর্ণাঙ্গ উপায় try/except, যা পর্ব ২৪-এর বিষয়। এখনকার জন্য একটি সহজ আগাম-যাচাই শিখে রাখুন।

আগে দেখে নিন: .isdigit()

প্রতিটি str-এর সাথে কিছু তৈরি method থাকে (পর্ব ০৯-এ .upper(), .strip() দেখেছেন)। .isdigit() জানায় লেখার সব অক্ষর অঙ্ক কি না — True বা False. শর্তের ওপর ভিত্তি করে আলাদা কাজ করা (if) পরের পর্বের বিষয়, তাই এখানে শুধু যাচাইয়ের ফলটা দেখুন:

print("25".isdigit())      # True
print("২৫".isdigit())      # True  — বাংলা অঙ্কও অঙ্ক
print("25.5".isdigit())    # False — বিন্দু অঙ্ক নয়
print("-5".isdigit())      # False — মাইনাস চিহ্ন অঙ্ক নয়
print(" 25".isdigit())     # False — space অঙ্ক নয়
print(" 25".strip().isdigit())  # True — আগে space ছাঁটুন

সীমাবদ্ধতাগুলো খেয়াল করুন: ঋণাত্মক সংখ্যা আর দশমিক সংখ্যা .isdigit() পাস করে না, অথচ int()/float() সেগুলো দিব্যি পড়তে পারে। আরেকটি সূক্ষ্ম ফাঁক: "²" (superscript দুই) .isdigit()-এ True দেয়, কিন্তু int("²") ValueError দেয়। একটু কড়া যাচাই চাইলে .isdecimal() — এটি শুধু সেই অঙ্কগুলোতে True দেয়, যা int() পড়তে পারে। তবু পেশাদার কোডে নিয়ম হলো “আগে জিজ্ঞেস নয়, চেষ্টা করে দেখো” — int() চালাও, ব্যর্থ হলে ধরো। Python-এর জগতে একে বলে EAFP (Easier to Ask Forgiveness than Permission); পর্ব ২৪-এ ঠিক এভাবেই লিখব:

# শুধু এক ঝলক — বিস্তারিত পর্ব ২৪-এ
try:
    age = int(input("বয়স: "))
except ValueError:
    print("দয়া করে শুধু সংখ্যা লিখুন, যেমন 25")

পুরো প্রোগ্রাম: BMI ক্যালকুলেটর

এবার সব জুড়ে একটি কাজের প্রোগ্রাম। BMI (Body Mass Index) হলো ওজনকে (kg) উচ্চতার (m) বর্গ দিয়ে ভাগ করলে যা আসে — স্বাস্থ্য-পরীক্ষায় বহুল ব্যবহৃত একটি মোটা দাগের সূচক। প্রোগ্রামটি তিনটি ইনপুট নেবে: নাম, ওজন আর উচ্চতা। খেয়াল করুন, এখানে আমরা ইচ্ছা করে ইনপুটের type ছাপিয়ে দেখছি:

# BMI ক্যালকুলেটর: নাম, ওজন (kg) আর উচ্চতা (m) পড়ে
name = input("Name: ")
weight_text = input("Weight (kg): ")
height_text = input("Height (m): ")
print()  # প্রম্পটগুলোর পরে একটি নতুন লাইন
print(type(weight_text), repr(weight_text))

weight = float(weight_text)   # লেখা থেকে সংখ্যা — নিজে বদলাতে হয়
height = float(height_text)
bmi = weight / height ** 2

print(f"Hello, {name}!")
print(f"{weight=}, {height=}")
print(f"{bmi=:.2f}")
print(f"আপনার BMI: {bmi:.1f}")

এই বক্সের ইনপুট আগে থেকে দেওয়া: তিন লাইনে রাফি, 70 আর 1.75. তাই প্রথম আউটপুট লাইনে তিনটি prompt পাশাপাশি — Name: Weight (kg): Height (m): — কারণ কেউ টাইপ করেনি, তাই উত্তরগুলো পর্দায় আসেনি (অংশ ১ মনে করুন)। print() শুধু একটি নতুন লাইন দিয়ে এই লাইনটি শেষ করে। পরের লাইন <class 'str'> '70' — প্রমাণ যে input() সংখ্যা নয়, লেখা দিয়েছে (repr() কোটসহ দেখায়, যাতে বোঝা যায় এটি str). float()-এর পরে হিসাব: ৭০ ÷ ১.৭৫² = ৭০ ÷ ৩.০৬২৫ ≈ ২২.৮৫৭. তারপর Hello, রাফি!; weight=70.0, height=1.75 (নাম আর মান একসাথে, {x=} — অংশ ৬-এ); bmi=22.86 (দুই দশমিকে গোল); আর শেষে আপনার BMI: 22.9. নিজের কম্পিউটারে চালালে প্রোগ্রাম প্রতিটি prompt-এ থেমে আপনার উত্তরের অপেক্ষা করবে।

C/C++ জানলে

C-র scanf("%d", &x) ফরম্যাট দেখে সরাসরি সংখ্যা পড়ে, আর ভুল ইনপুটে চুপচাপ ব্যর্থ হয় (return value না দেখলে টেরই পাবেন না)। Python-এ উল্টো: input() সবসময় পুরো একটি লাইন str হিসেবে দেয়, রূপান্তর আপনি করেন, আর ভুল হলে জোরে ValueError — চুপচাপ ভুল মান নয়। আউটপুটে printf("%8.2f", x)-এর প্রায় হুবহু রূপ f"{x:8.2f}"; Python-এর পুরোনো %-formatting তো সরাসরি printf থেকেই নেওয়া। C-র দিকটি বিস্তারিত: C কোর্সের printf ও scanf পর্ব।

৪ · এক লাইনে কয়েকটি মান — split() আর map()

input() সবসময় একটি পুরো লাইন পড়ে। ব্যবহারকারী যদি এক লাইনে 3 5 লেখেন, আপনি পাবেন একটিমাত্র str: '3 5'. প্রোগ্রামিং কনটেস্টে আর অনেক ডেটা-ফাইলে এক লাইনে কয়েকটি সংখ্যা থাকাই নিয়ম — তাই এই লাইনটি Python-এর সবচেয়ে বেশি লেখা লাইনগুলোর একটি:

a, b = map(int, input().split())

এক লাইনে তিনটি কাজ হচ্ছে, তাই প্রথম দেখায় জাদুর মতো লাগে। ভেতর থেকে বাইরে, ধাপে ধাপে খুলে দেখি:

ধাপ ১input()
stdin:  "3 5\n"
input()  →  '3 5'
একটি str — মাঝে একটি space
ধাপ ২.split()
'3 5'.split()  →  ['3', '5']
ফাঁকা জায়গায় কেটে টুকরোগুলোর list
টুকরোগুলো এখনো str!

'  3    5  '.split()  →  ['3', '5']
ধাপ ৩map(int, ...)
map(int, ['3', '5'])
প্রতিটি টুকরোর ওপর int() চালানোর প্রতিশ্রুতি:
int('3') → 3
int('5') → 5
ধাপ ৪a, b = ...
a, b = 3, 5
a → 3   b → 5
print(a + b)  →  8

ইনপুট "3 5 7" হলে:
ValueError: too many values to unpack (expected 2)
  1. input() — একটি লাইন, একটি str. এখানে কোনো ভাগাভাগি নেই। '3 5' হলো তিন অক্ষরের একটি লেখা: “3”, একটি space, “5”.
  2. .split() — টুকরো করা। বন্ধনী খালি রাখলে এই str method যেকোনো পরিমাণ ফাঁকা জায়গায় (space, tab) কাটে, আর দুই পাশের বাড়তি ফাঁকা বাদ দেয়। ফল একটি list — পর্ব ১৪-এ বিস্তারিত। কমা দিয়ে আলাদা করা হলে .split(","). কিন্তু খেয়াল করুন: টুকরোগুলো এখনো লেখা।
  3. map(int, ...) — প্রতিটিকে রূপান্তর। map একটি ফাংশন আর একসারি জিনিস নেয়, আর প্রতিটি জিনিসের ওপর ফাংশনটি চালায়। লক্ষ করুন, int লেখা হয়েছে বন্ধনী ছাড়া — আমরা ফাংশনটিকে ডাকছি না, map-এর হাতে তুলে দিচ্ছি, যাতে সে নিজে প্রতিটি টুকরোর জন্য ডাকে। map অলস: ফলগুলো তখনই হিসাব হয়, যখন কেউ চায়।
  4. a, b = ... — ভাগ করে দেওয়া। বাঁ দিকে দুটি নাম, ডান দিকে দুটি মান — প্রথমটি a-তে, দ্বিতীয়টি b-তে। একে বলে unpacking (পর্ব ১৫-এ পুরো গল্প)। সংখ্যা না মিললে — তিনটি মান, দুটি নাম — ValueError. আর সংখ্যা জানা না থাকলে nums = list(map(int, input().split())) লিখে সবগুলো একটি list-এ রাখা যায়।

উদাহরণ: বিদ্যুৎ বিলের আনুমানিক হিসাব

ধরা যাক মিটার রিডিংয়ের আগের আর এখনকার সংখ্যা এক লাইনে দেওয়া হলো। ইউনিটপ্রতি একটি দর আর একটি নির্দিষ্ট মাসিক চার্জ ধরে আনুমানিক বিল বের করব। (দরগুলো উদাহরণের জন্য বানানো; বাংলাদেশে আসল বিল ধাপে ধাপে বাড়া দর, ডিমান্ড চার্জ আর VAT মিলিয়ে হয়, আর ধাপের হিসাব করতে if লাগবে — পর্ব ১১।)

prev, now = map(int, input("আগের ও এখনকার রিডিং: ").split())
units = now - prev
rate = 8.5          # ইউনিটপ্রতি টাকা (কাল্পনিক দর)
fixed = 42          # মাসিক নির্দিষ্ট চার্জ (কাল্পনিক)
bill = units * rate + fixed
print(f"{units} unit x {rate} + {fixed} = {bill:,.2f} Tk")

$ python bill.py
আগের ও এখনকার রিডিং: 10450 10712
262 unit x 8.5 + 42 = 2,269.00 Tk

শেষ লাইনের {bill:,.2f} আপনাকে বিলের মতো চেহারা দিয়েছে — হাজারের ঘরে কমা, দুই দশমিক। এই ছোট্ট সংকেতগুলোর ব্যাকরণই এখন শিখব।

৫ · f-string — ফাঁকা ঘরওয়ালা ফর্ম

ব্যাংকের জমা-স্লিপের কথা ভাবুন: “নাম: ______, হিসাব নম্বর: ______, টাকা: ______”। ছাপানো লেখাটা সবার জন্য এক, শুধু ফাঁকা ঘরগুলো প্রতিবার আলাদা মানে ভরা হয়। f-string ঠিক এমন একটি ফর্ম। string-এর কোটের আগে একটি f বসান, আর যেখানে মান বসবে সেখানে কোঁকড়া বন্ধনী { } দিন:

name = "সুমাইয়া"
taka = 500
print(f"{name} আপনাকে {taka} টাকা পাঠিয়েছেন")
সুমাইয়া আপনাকে 500 টাকা পাঠিয়েছেন

পর্ব ০৪-এর কমা-পদ্ধতি — print(name, "আপনাকে", taka, "টাকা পাঠিয়েছেন") — একই ফল দিত, কিন্তু কোথায় space বসবে তা নিয়ন্ত্রণ করা কঠিন, আর লেখাটি পড়ে বাক্যের চেহারা বোঝা যায় না। f-string-এ আপনি যা দেখছেন, প্রায় হুবহু তা-ই ছাপা হবে। আর ফলটি একটি সাধারণ str — চাইলে ছাপানোর বদলে variable-এ রেখে পরে ব্যবহার করা যায়।

{ }-এর ভেতরে যেকোনো expression

বন্ধনীর ভেতরে শুধু variable-এর নাম নয়, যেকোনো expression লেখা যায় — হিসাব, method-এর ডাক, এমনকি ছোট শর্ত। Python প্রতিটি { }-এর ভেতরটা হিসাব করে, ফলটিকে লেখায় বদলায়, তারপর জায়গামতো বসায়:

কোড (name = "rafi", qty = 3)ফলকী হলো
f"{qty * 15} টাকা"45 টাকাহিসাব আগে, তারপর বসানো
f"{name.title()}"Rafimethod ডাকা যায়
f"{len(name)} অক্ষর"4 অক্ষরফাংশন ডাকা যায়
f"{name!r}"'rafi'!r = repr() রূপ, কোটসহ
f"{{qty}} = {qty}"{qty} = 3আসল { ছাপাতে দুবার লিখুন
f"{'জোড়' if qty % 2 == 0 else 'বিজোড়'}"বিজোড়এক লাইনের শর্ত (পর্ব ১১)

একটি পুরোনো নিষেধাজ্ঞা আর নেই, তবে পুরোনো কোড বা পুরোনো টিউটোরিয়ালে দেখবেন বলে জেনে রাখুন। Python 3.12-এর আগে f-string-এর ভেতরে বাইরের কোটটি আবার ব্যবহার করা যেত না — f"{d["key"]}" লিখলে SyntaxError, তাই লিখতে হতো f"{d['key']}". ভেতরে backslash আর comment-ও নিষেধ ছিল। Python 3.12 থেকে (PEP 701) এই বাধাগুলো উঠে গেছে। তবু পড়ার সুবিধার জন্য ভেতরে অন্য কোট ব্যবহারই ভালো অভ্যাস, আর পুরোনো Python-এও চলবে।

সবচেয়ে সাধারণ ভুল: সামনের f ভুলে যাওয়া। print("{name} আপনাকে...") কোনো error দেয় না — হুবহু {name} আপনাকে... ছাপে। চুপচাপ ভুল, তাই আউটপুটে কোঁকড়া বন্ধনী দেখলেই প্রথমে f খুঁজুন।

পরীক্ষার প্রবেশপত্র ছাপানোর কথা ভাবুন। বোর্ড একটিই নকশা বানায় — “নাম: {name}, রোল: {roll}, কেন্দ্র: {center}” — আর লক্ষ পরীক্ষার্থীর জন্য ফাঁকা ঘরে আলাদা মান বসিয়ে ছাপে। নকশা বদলালে সবার কার্ড একসাথে বদলায়। f-string-এ আপনি সেই নকশাকার: লেখার কাঠামো একবার ঠিক করেন, মানগুলো প্রতিবার বসে।

৬ · Format spec — মান কেমন দেখাবে

{ }-এর ভেতরে একটি কোলন : দিলে তার পরের অংশ বলে মানটি কেমন দেখাবে — কত ঘর জায়গা নেবে, বাঁয়ে না ডানে বসবে, কত দশমিক, কমা থাকবে কি না। কোলনের পরের এই ছোট সংকেতকে বলে format spec, আর এর নিয়মগুলোর সমষ্টিকে Python-এর ডকুমেন্টেশন বলে “format specification mini-language” — একটি ভাষার ভেতরে আরেকটি ছোট্ট ভাষা। প্রথম দেখায় {price:>10,.2f} হায়ারোগ্লিফের মতো লাগে; চলুন অংশে অংশে জোড়া লাগাই। ধরা যাক price = 1234.5:

f"{price : > 10 , .2 f }"
f"[{price}]"কোনো spec নেই
[1234.5]
যেমন আছে তেমন — print(price)-এর মতো
f"[{price:f}]"type: f
[1234.500000]
f = fixed-point দশমিক; default ৬ ঘর
f"[{price:.2f}]"precision: .2
[1234.50]
দশমিকের পরে ঠিক ২ ঘর (গোল করে)
f"[{price:,.2f}]"grouping: ,
[1,234.50]
প্রতি তিন অঙ্কে কমা (হাজার, মিলিয়ন)
f"[{price:10,.2f}]"width: 10
[  1,234.50]
মোট ১০ ঘর; সংখ্যা নিজে থেকে ডানে
f"[{price:>10,.2f}]"align: >
>  [  1,234.50]   ডানে
<  [1,234.50  ]   বাঁয়ে
^  [ 1,234.50 ]   মাঝে
  1. শুধু মান। কোলন নেই, তাই Python মানটিকে তার স্বাভাবিক রূপে লেখায় বদলায় — 1234.5. চারপাশের [ ] শুধু ফাঁকা জায়গা দৃশ্যমান করার জন্য দেওয়া।
  2. কোলন খোলে spec, আর শেষের অক্ষর হলো type. f মানে fixed-point দশমিক সংখ্যা। কিছু না বললে দশমিকের পরে ৬ ঘর দেখায়। পূর্ণসংখ্যার জন্য d, শতাংশের জন্য %, বৈজ্ঞানিক রূপের জন্য e.
  3. .2 — precision. বিন্দুর পরের সংখ্যাটি বলে দশমিকের পরে কত ঘর। .2f টাকা-পয়সার জন্য আদর্শ। গোল করা হয় — তবে float-এর ভেতরের বাইনারি রূপের কারণে মাঝামাঝি মানে (যেমন 2.675) কখনো চমক আসে, পর্ব ০৭ মনে করুন।
  4. , — হাজারের কমা। পশ্চিমা নিয়মে প্রতি তিন অঙ্কে কমা। কমার বদলে আন্ডারস্কোর চাইলে _. বাংলাদেশের লাখ-কোটির নিয়ম (১২,৩৪,৫৬৭) এখানে নেই — একটু পরে দেখবেন কী করা যায়।
  5. 10 — width, মোট কত ঘর। ফলটি ১০ অক্ষরের চেয়ে ছোট হলে বাকি জায়গা space দিয়ে ভরে। সংখ্যা স্বভাবত ডানে সরে, লেখা বাঁয়ে। মান width-এর চেয়ে বড় হলে কাটা পড়ে না — width শুধু কমপক্ষে কত ঘর, সেটা বলে।
  6. > — alignment. > ডানে, < বাঁয়ে, ^ মাঝে। এর ঠিক আগে একটি অক্ষর দিলে সেটা দিয়ে ফাঁকা ভরে: {"ok":*^8} দেয় ***ok***. পুরো ক্রমটা মনে রাখুন: [ভরাট][দিক][চিহ্ন][0][চওড়া][কমা][.দশমিক][type] — কোনো অংশ বাদ দেওয়া যায়, কিন্তু ক্রম বদলানো যায় না।

সবচেয়ে কাজের spec-গুলো এক টেবিলে

কোডফল ([ ] দিয়ে ঘেরা)কাজ
f"[{'ab':>8}]"[ ab]৮ ঘর, ডানে
f"[{'ab':<10}]"[ab ]১০ ঘর, বাঁয়ে
f"[{'ab':^10}]"[ ab ]১০ ঘর, মাঝে
f"{3.14159:.2f}"3.14দুই দশমিক
f"{12345678:,}"12,345,678হাজারের কমা
f"{0.4567:.1%}"45.7%১০০ দিয়ে গুণ করে শতাংশ
f"{7:05d}"00007শূন্য দিয়ে ভরা, ৫ ঘর (রোল, রসিদ নম্বর)
f"{42:+d}"+42ধনাত্মক হলেও চিহ্ন দেখানো
f"{255:b}", f"{255:x}"11111111, ffবাইনারি, হেক্সাডেসিমাল
f"[{'tea':>{w}}]" (w = 6)[ tea]width নিজেও একটি variable থেকে

শেষ সারিটি খেয়াল করুন: spec-এর ভেতরে আবার {w}. একে বলে nested বা ভেতরে-ভেতরে বসানো spec — চওড়া বা দশমিকের সংখ্যা প্রোগ্রাম চলার সময় ঠিক হলে খুব কাজের, যেমন সবচেয়ে লম্বা নাম দেখে কলামের চওড়া ঠিক করা। আর % type মানটিকে নিজেই ১০০ দিয়ে গুণ করে — তাই 0.457 দিন, 45.7 নয়; নইলে 4570.0% দেখবেন।

{x=} — ডিবাগিংয়ের ছোট্ট উপহার

কোড ঠিকমতো চলছে কি না দেখতে প্রোগ্রামাররা প্রায়ই লেখেন print("total =", total). নামটা দুবার লিখতে হয়, আর কপি-পেস্টে নাম বদলাতে ভুলে গেলে ভুল লেবেল ছাপে। Python 3.8 থেকে f-string-এ = দিলে expression-এর লেখাটা আর তার মান — দুটোই একসাথে আসে:

x = 42
print(f"{x=}")          # x=42
print(f"{x = }")        # x = 42   (space-ও হুবহু থাকে)
print(f"{x * 2=}")      # x * 2=84
print(f"{3.14159=:.2f}")  # 3.14159=3.14  (= এর পরে spec-ও চলে)
name = "rafi"
print(f"{name=}")       # name='rafi'  (str হলে কোটসহ, repr রূপ)

শেষ লাইনটি গুরুত্বপূর্ণ: = দিলে str মান কোটসহ দেখায়, ফলে ' rafi'-এর মতো লুকানো space-ও ধরা পড়ে — ইনপুটের ঝামেলা খোঁজার সময় এটাই চাই। BMI প্রোগ্রামে {weight=} আর {bmi=:.2f} এ জন্যই রাখা হয়েছিল।

লাখ-কোটির কমা — নিজেই বানাই

বাংলাদেশ আর ভারতে বড় সংখ্যা লেখা হয় প্রথমে তিন অঙ্ক, তারপর দুই-দুই অঙ্কে: ১,২৩,৪৫,৬৭৮ (এক কোটি তেইশ লাখ…)। Python-এর , spec শুধু তিন-তিন ভাগ জানে। অপারেটিং সিস্টেমের locale ব্যবস্থা দিয়ে (locale module আর n type) কখনো কখনো এটা পাওয়া যায়, কিন্তু সেটা নির্ভর করে কম্পিউটারে কোন locale ইনস্টল আছে তার ওপর — সব জায়গায় একই ফল দেয় না। ভরসাযোগ্য উপায় হলো ছোট একটি নিজস্ব ফাংশন। পুরোটা বুঝতে পর্ব ১২ (লুপ) আর ১৩ (ফাংশন) লাগবে; এখন শুধু ধারণাটা দেখুন — শেষের তিন অঙ্ক আলাদা, বাকিটা ডান দিক থেকে দুই-দুই করে কাটা:

def lakh(n):
    s = str(n)
    if len(s) <= 3:
        return s
    head, tail = s[:-3], s[-3:]       # শেষের তিন অঙ্ক আলাদা
    parts = []
    while len(head) > 2:
        parts.insert(0, head[-2:])    # ডান দিক থেকে দুই-দুই
        head = head[:-2]
    parts.insert(0, head)
    return ",".join(parts) + "," + tail

print(lakh(12345678))     # 1,23,45,678
print(lakh(100000))       # 1,00,000

৭ · সাজানো টেবিল — মাসিক খরচের রিপোর্ট

পর্ব ০৪-এর রসিদে কলাম সোজা রাখতে space গুনে গুনে লিখতে হয়েছিল। একটি নাম লম্বা হলেই পুরো টেবিল আবার সাজাতে হতো। Width আর alignment দিয়ে এখন সেই খাটুনি Python-কে দিই। ঢাকার একটি পরিবারের মাসিক খরচ এক লাইনে ইনপুট হিসেবে আসছে — বাসা ভাড়া, বাজার, যাতায়াত আর ইন্টারনেট-মোবাইল। আমরা চাই একটি পরিষ্কার রিপোর্ট: নাম বাঁয়ে, টাকা ডানে কমাসহ, আর মোটের কত শতাংশ:

# মাসিক খরচের রিপোর্ট: এক লাইনে চারটি খরচ (টাকা) পড়ে
rent, bazar, transport, net = map(int, input().split())
total = rent + bazar + transport + net
w = 12  # নামের কলামের চওড়া

print(f"{'Item':<{w}}{'Taka':>10}{'Share':>8}")
print("-" * (w + 18))
print(f"{'House rent':<{w}}{rent:>10,}{rent / total:>8.1%}")
print(f"{'Bazar':<{w}}{bazar:>10,}{bazar / total:>8.1%}")
print(f"{'Transport':<{w}}{transport:>10,}{transport / total:>8.1%}")
print(f"{'Net+Mobile':<{w}}{net:>10,}{net / total:>8.1%}")
print("-" * (w + 18))
print(f"{'Total':<{w}}{total:>10,}{'100%':>8}")
print(f"Daily average: {total / 30:,.2f} Tk")

এই বক্সের ইনপুট আগে থেকে দেওয়া: এক লাইনে 15000 6500 1800 1200. কোনো prompt নেই, তাই আউটপুট সরাসরি টেবিল দিয়ে শুরু। প্রতিটি সারিতে নামের কলাম ১২ ঘর বাঁয়ে (<{w}), টাকা ১০ ঘর ডানে কমাসহ (>10,), শতাংশ ৮ ঘর ডানে এক দশমিকে (>8.1%). ফল: House rent 15,000 61.2%, Bazar 6,500 26.5%, Transport 1,800 7.3%, Net+Mobile 1,200 4.9%, তারপর Total 24,500 100% আর Daily average: 816.67 Tk. সব সংখ্যা ডান দিকে একই কলামে শেষ হয়েছে — কোথাও space গুনতে হয়নি। শতাংশগুলো যোগ করলে ৯৯.৯ হয়, ১০০ নয়: প্রতিটি আলাদাভাবে গোল করা হয়েছে, এটা বাস্তব রিপোর্টেও ঘটে, আর সেজন্য মোটের ঘরে হিসাব না করে '100%' লেখা হয়েছে। একই ধাঁচের চারটি লাইন বারবার লেখা দৃষ্টিকটু লাগছে? ঠিক ধরেছেন — পর্ব ১২-এ লুপ দিয়ে এটি দুই লাইনে নামবে।

বাংলা লেখায় কলাম কেন বাঁকা হয়

টেবিলে জিনিসের নাম ইংরেজিতে কেন? 'Bazar'-এর জায়গায় 'বাজার' লিখলেই তো হতো! চেষ্টা করলে দেখবেন, সংখ্যার কলাম আর সোজা থাকে না। কারণটা সৎভাবে বলা দরকার, কারণ এটা Python-এর দোষ নয়।

Width গোনার সময় Python গোনে code point — Unicode-এর প্রতিটি আলাদা চিহ্ন, যা len() দেখায়। কিন্তু পর্দায় আমরা দেখি অক্ষরের দৃশ্যমান রূপ। বাংলায় কার-চিহ্ন (ি, ে), হসন্ত (্), ফলা আলাদা code point হলেও আলাদা ঘর নেয় না — আগের ব্যঞ্জনের সাথে মিশে যায়। ফলে:

print(len("কলম"), len("কি"), len("কম্পিউটার"))   # 3 2 9
print(f"[{'কলম':<8}]")        # [কলম     ]   ৩ চিহ্ন + ৫ space
print(f"[{'কি':<8}]")         # [কি      ]   ২ চিহ্ন + ৬ space, কিন্তু দেখতে একটি অক্ষর!
print(f"[{'কম্পিউটার':<8}]")  # [কম্পিউটার]  ৯ চিহ্ন — width-এর চেয়ে বড়, কোনো space নেই

“কি” দেখতে একটি অক্ষর, কিন্তু Python দুটি গুনেছে, তাই একটি space কম দিয়েছে। “কম্পিউটার” দেখতে ছয়-সাতটি ঘরের মতো, Python গোনে ৯. এর ওপর আছে ফন্টের সমস্যা: বেশিরভাগ monospace ফন্টে বাংলা অক্ষরই থাকে না, তাই টার্মিনাল অন্য ফন্ট থেকে অক্ষর ধার করে, যার চওড়া ইংরেজি অক্ষরের সমান নয়। একই সমস্যা হয় চীনা-জাপানি অক্ষর (যেগুলো দুই ঘর চওড়া) আর emoji-তেও।

বাস্তব সমাধান

(১) টার্মিনালের টেবিলে সারিবদ্ধ কলাম দরকার হলে লেবেল ইংরেজিতে বা রোমান হরফে রাখুন, আর বাংলা লেখা রাখুন শেষ কলামে — শেষ কলাম বাঁকা হলেও কারও ক্ষতি নেই। (২) সত্যিকারের সুন্দর বাংলা রিপোর্ট দরকার হলে টার্মিনাল নয়, HTML, PDF বা স্প্রেডশিট ব্যবহার করুন — সেখানে টেবিলের ঘর ফন্টের মাপ নিজেই সামলায় (CSV ফাইল লেখা পর্ব ২৬-এ)। (৩) বাইরের লাইব্রেরি wcwidth পূর্ব এশীয় অক্ষর আর emoji-র দৃশ্যমান চওড়া মাপে, কিন্তু বাংলার মতো লিপিতে ফলাফল ফন্ট ও টার্মিনাল-ভেদে বদলাতে পারে — অন্ধভাবে ভরসা করবেন না, পরীক্ষা করে নিন।

রসিদ — f-string দিয়ে নতুন করে

পর্ব ০৪-এর মামার টং-এর রসিদটি এবার নতুন হাতিয়ারে লিখলে কেমন দাঁড়ায়, দেখুন। লক্ষ করুন, কোথাও space গোনা নেই, আর দাম একবারই লেখা:

tea, singara = 15, 10
print(f"{'Item':<10}{'Qty':>4}{'Price':>8}")
print(f"{'Milk tea':<10}{2:>4}{2 * tea:>8.2f}")
print(f"{'Singara':<10}{3:>4}{3 * singara:>8.2f}")
print(f"{'Total':<14}{2 * tea + 3 * singara:>8.2f}")
print(f"{'Receipt no.':<14}{7:>8}  ->  #{7:05d}")

আউটপুট:
Item       Qty   Price
Milk tea     2   30.00
Singara      3   30.00
Total            60.00
Receipt no.          7  ->  #00007
পরীক্ষা ও ইন্টারভিউতে
  • input() কোন type ফেরত দেয়? — সবসময় str, ব্যবহারকারী সংখ্যা লিখলেও; শেষের newline বাদ দিয়ে।
  • ব্যবহারকারী 5 ও 3 দিলে print(input() + input())-এর আউটপুট? — 53; দুটি str জোড়া লাগে।
  • int("3.5") কী দেয়? — ValueError; আগে float() লাগবে।
  • এক লাইন থেকে দুটি পূর্ণসংখ্যা পড়ার Pythonic উপায়? — a, b = map(int, input().split()).
  • f"{0.25:.0%}" আর f"{5:03d}"-এর ফল? — 25% আর 005.

৮ · পুরোনো ধাঁচ, stderr আর flush

পুরোনো কোডে যে দুই ধাঁচ দেখবেন

f-string এসেছে Python 3.6-এ (২০১৬)। তার আগের কোটি কোটি লাইন কোড, অনেক বই আর Stack Overflow-এর উত্তরে আরও দুটি পদ্ধতি দেখবেন। নিজে নতুন কোডে f-string-ই লিখুন, কিন্তু এগুলো পড়তে জানা দরকার:

name, taka = "Rafi", 1234.5
print(f"{name} paid {taka:,.2f} Tk")
# Rafi paid 1,234.50 Tk

মান সরাসরি জায়গামতো, পড়তে সবচেয়ে সহজ, আর সাধারণত সবচেয়ে দ্রুতও।

print("{} paid {:,.2f} Tk".format(name, taka))
print("{0} paid {1:,.2f} Tk; {0} is happy".format(name, taka))
print("{n} paid {t:,.2f} Tk".format(n=name, t=taka))

str.format() এসেছে Python 3.0 আর 2.6-এ। { } আর :-এর পরের spec হুবহু একই mini-language — f-string আসলে এই ব্যবস্থাই ব্যবহার করে। পার্থক্য হলো মান দিতে হয় শেষে, আলাদা করে। এখনো কাজের একটি জায়গা আছে: নকশা আগে থেকে ঠিক করা, মান পরে আসবে — যেমন একটি টেমপ্লেট-লেখা ফাইল থেকে পড়া।

print("%s paid %.2f Tk" % (name, taka))
print("Roll: %05d" % 7)
# Rafi paid 1234.50 Tk
# Roll: 00007

%-formatting সরাসরি C-র printf থেকে নেওয়া: %s লেখা, %d পূর্ণসংখ্যা, %.2f দুই দশমিক। পুরোনো কোডে আর Python-এর logging module-এ (পর্ব ৪০) এখনো খুব দেখা যায়। ঝামেলা: মান একটিমাত্র tuple হলে বিভ্রান্তিকর error, আর , হাজার-কমার মতো নতুন সুবিধা নেই।

error-বার্তা আলাদা নলে — stderr

অংশ ১-এ তিনটি stream-এর কথা বলেছিলাম। print()-এর একটি keyword argument আছে file, যা দিয়ে লেখা stdout-এর বদলে stderr-এ পাঠানো যায়:

import sys
print("রিপোর্ট তৈরি হলো")                        # stdout
print("সতর্কতা: ২টি সারি বাদ পড়েছে", file=sys.stderr)  # stderr

পর্দায় দুটোই একই রকম দেখায়, তাহলে আলাদা নল কেন? কারণ কেউ যখন আউটপুট একটি ফাইলে পাঠায় (python report.py > report.txt), তখন শুধু stdout ফাইলে যায়; stderr-এর সতর্কবার্তা তখনো পর্দায় আসে। ফলে ফাইলটি পরিষ্কার ডেটা থাকে, আর মানুষ সমস্যার খবরও পান। ভালো কমান্ড-লাইন টুলের নিয়ম: ফল stdout-এ, খবর ও সতর্কতা stderr-এ। বড় প্রোগ্রামে এই কাজ আরও গুছিয়ে করে logging module — পর্ব ৪০।

এই কোর্সের চালানো-যায় এমন কোড-বক্সগুলোতে stderr-এ কিছু লেখা হয় না, কারণ আমাদের যাচাই-ব্যবস্থা stderr-এ কিছু দেখলে সেটাকে error হিসেবে ধরে। নিজের কম্পিউটারে file=sys.stderr নির্দ্বিধায় পরীক্ষা করুন।

লেখা দেরিতে আসে কেন — flush

print() প্রতিটি লেখা সাথে সাথে পর্দায় পাঠায় না। সময় বাঁচাতে লেখাগুলো আগে একটি buffer-এ জমে — পোস্ট অফিসের থলের মতো, ভরলে বা নির্দিষ্ট সময়ে একসাথে পাঠানো হয়। টার্মিনালে সাধারণত প্রতিটি নতুন লাইনে থলে খালি হয়। কিন্তু আউটপুট ফাইল বা অন্য প্রোগ্রামে গেলে বড় বড় খণ্ডে জমে পাঠানো হয়, আর end="" দিয়ে লেখা অর্ধেক লাইন টার্মিনালেও আটকে থাকতে পারে। “Loading…” লিখে একটি লম্বা কাজ শুরু করলেন, অথচ লেখাটা কাজ শেষের পরে দেখা দিল — এই হলো লক্ষণ। সমাধান:

print("ডাউনলোড হচ্ছে...", end="", flush=True)   # থলে এখনই খালি করো

# অথবা পুরো প্রোগ্রামের জন্য buffer বন্ধ:
# $ python -u script.py

flush argument এসেছে Python 3.3-এ। আর input()-এর prompt নিয়ে চিন্তা নেই — input() নিজেই অপেক্ষা শুরুর আগে prompt দেখিয়ে দেয়। C-তে fflush(stdout) যে কাজ করে, এটি সেই কাজ।

৯ · AI-এর যুগে — ইনপুট-আউটপুটের কোড যাচাই

“একটি BMI ক্যালকুলেটর লিখে দাও” বললে যেকোনো AI কয়েক সেকেন্ডে কোড দেবে, আর বেশিরভাগ সময় সেটা চলবে। কিন্তু ইনপুট-আউটপুটের কোডে কয়েকটি ভুল AI বারবার করে, কারণ সে শিখেছে নানা যুগের, নানা মানের কোড থেকে। এগুলো চোখে ধরার ক্ষমতাই আপনাকে AI-এর ওপর নির্ভরশীল নয়, AI-এর পরিচালক বানাবে।

AI যা লিখতে পারেসমস্যাযা হওয়া উচিত
age = input("Age: ") তারপর age + 1input() যে str দেয় তা ভুলে যাওয়া — TypeError, বা + হলে চুপচাপ জোড়া লাগাint(input(...))
x = eval(input())ব্যবহারকারীর লেখা কোড হিসেবে চালায় — মারাত্মক নিরাপত্তা-ঝুঁকি (নিচে দেখুন)int()/float(); জটিল মানের জন্য ast.literal_eval
একই ফাইলে "%s" % x, "{}".format(x) আর f-string মেশানোপড়তে কঠিন, ধারাবাহিকতা নেই — পুরোনো উদাহরণ থেকে টুকরো জোড়ানতুন কোডে শুধু f-string
Python 2-এর raw_input()Python 3-এ নেই — NameErrorinput()
দাম round(x, 2) করে ছাপানো2.5 ছাপে 2.5, 2.50 নয়f"{x:.2f}"
কনটেস্টের কোডে input("Enter n: ")prompt-ও stdout-এ যায়, বিচারকের উত্তর মেলে নাকনটেস্টে prompt ছাড়া input()

eval(input()) কেন বিপজ্জনক

eval() একটি লেখাকে Python expression হিসেবে চালায়। eval("2 + 3") দেয় 5 — তাই অনেক পুরোনো টিউটোরিয়াল “সংখ্যা পড়ার সহজ উপায়” হিসেবে eval(input()) শেখাত, কারণ এটা 25-কে int আর 2.5-কে float বানিয়ে দেয়। সমস্যা হলো, ব্যবহারকারী সংখ্যার বদলে যা-ই লিখুন, সেটাও চলবে — আপনার প্রোগ্রামের সমস্ত ক্ষমতা নিয়ে। ফাইল পড়া, মুছে ফেলা, অন্য প্রোগ্রাম চালানো — যা কিছু আপনার কোড করতে পারে, ইনপুট দেওয়া মানুষটিও তা করতে পারবেন। প্রোগ্রামটি ওয়েবসাইট বা বটের পেছনে চললে সেই মানুষটি পৃথিবীর যেকোনো কেউ। এই ধরনের আক্রমণকে বলে code injection. নিয়মটা সহজ: বাইরে থেকে আসা লেখা কখনো eval বা exec-এ দেবেন না। নিরাপদ Python নিয়ে বিস্তারিত পর্ব ৫০-এ।

AI-কে ইনপুট-আউটপুটের কোড লিখতে বললে নিচের কাঠামোটি ব্যবহার করুন। শর্তগুলো আগে থেকে বলে দিলে উপরের টেবিলের বেশিরভাগ ভুল শুরুতেই আটকে যায়:

Python 3.12 বা নতুন version-এর জন্য একটি ছোট প্রোগ্রাম লিখে দাও: [কী করবে — যেমন: ওজন ও উচ্চতা পড়ে BMI দেখাবে]। শর্ত: ১. input() যে str দেয় তা মাথায় রাখো; প্রতিটি সংখ্যা int() বা float() দিয়ে বদলাও। ২. eval() বা exec() কোনোভাবেই ব্যবহার করবে না। ৩. আউটপুটে শুধু f-string; % বা .format() মেশাবে না। টাকার মান :,.2f দিয়ে দেখাও। ৪. ভুল ইনপুট (খালি লাইন, অক্ষর, দশমিক) এলে কী হবে, তা কোডের নিচে আলাদা করে বলো। ৫. আমি শিখছি — প্রতিটি format spec-এর মানে এক লাইনে বুঝিয়ে দাও।

AI-এর কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

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

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

প্র ০১Python-এ লেখা সাজানোর তিনটি পদ্ধতি থাকতেও ২০১৬ সালে চতুর্থটি — f-string — কেন আনা হলো? আর Python 3.12-এ এর “নিয়ম লেখা” হলো মানে কী?

২০১৫ সালের আগে Python-এ লেখার ভেতরে মান বসানোর তিনটি উপায় ছিল। সবচেয়ে পুরোনো %-formatting, C-র printf থেকে নেওয়া। তারপর PEP 292 (Python 2.4) এনেছিল string.Template — $name ধাঁচের সরল টেমপ্লেট। আর PEP 3101 এনেছিল str.format(), যা Python 3.0 ও 2.6-এ যুক্ত হয়। তিনটিরই একটি সাধারণ দুর্বলতা: মান আর তার জায়গা আলাদা। "{} paid {} for {}".format(a, b, c) পড়তে গেলে চোখকে বারবার বাঁ থেকে ডানে ছুটতে হয় কোন {}-এ কোন মান বসছে তা মেলাতে; লম্বা লেখায় একটি মান সরে গেলেই ভুল লেবেল।

২০১৫ সালে Python core developer Eric V. Smith লিখলেন PEP 498 — “Literal String Interpolation”. প্রস্তাব সোজা: string-এর আগে f দিলে { }-এর ভেতরের expression সেই জায়গাতেই, সেই মুহূর্তে হিসাব হবে। Ruby, JavaScript-এর template literal, Swift-সহ অনেক ভাষায় তখন এমন “interpolation” ছিল। PEP-টি গৃহীত হয় আর ২০১৬ সালের ডিসেম্বরে Python 3.6-এর সাথে আসে। লক্ষ করুন, f-string নতুন formatting ভাষা আনেনি — :-এর পরের spec হুবহু str.format()-এর mini-language. নতুনত্ব শুধু মান কোথায় লেখা হয়, তাতে। গতির সুবিধাও ছিল: f-string সরাসরি bytecode-এ রূপ নেয়, .format() method খোঁজা ও ডাকার খরচ লাগে না।

কিন্তু প্রথম সংস্করণে f-string ছিল কিছুটা “জোড়াতালি”: tokenizer পুরো f-string-কে একটি সাধারণ string হিসেবে পড়ত, তারপর আলাদা একটি ছোট parser ভেতরের { } খুঁজে বের করত। এর ফলেই সেই অদ্ভুত নিষেধ — ভেতরে একই কোট চলবে না, backslash চলবে না, comment চলবে না। ২০১৯ সালে Python 3.8-এ Eric Smith-ই যোগ করেন {x=} ডিবাগিং সুবিধা। তারপর ২০২২ সালে Pablo Galindo Salgado, Batuhan Taskaya, Lysandros Nikolaou ও Marta Gómez Macías লেখেন PEP 701 — “Syntactic formalization of f-strings”, যা Python 3.12-এ (২০২৩) আসে। এখন f-string Python-এর মূল ব্যাকরণের (PEG grammar) অংশ: tokenizer এর ভেতরের অংশগুলোকে আলাদা token হিসেবে চেনে। ফলে বেশিরভাগ পুরোনো নিষেধ উঠে গেছে, error মেসেজ নির্ভুল জায়গা দেখায়, আর অন্য Python implementation-গুলোর জন্যও নিয়ম পরিষ্কার হয়েছে।

এখানে Python-এর একটি পরিচিত টানাপোড়েন দেখা যায়: “Zen of Python” বলে কোনো কাজের একটি স্পষ্ট উপায় থাকা উচিত, অথচ এখন চারটি। বাস্তবতা হলো, পুরোনো পদ্ধতি সরিয়ে দিলে কোটি লাইন কোড ভাঙত — তাই নতুন উপায় যোগ হয়, পুরোনোটা থেকে যায়, আর সমাজ ধীরে ধীরে নতুনটিকে “স্বাভাবিক” হিসেবে মেনে নেয়।

মূল উপলব্ধি: ভালো নকশা প্রায়ই কোনো নতুন ক্ষমতা আনে না, শুধু জিনিসকে সেই জায়গায় সরিয়ে দেয় যেখানে মানুষ খোঁজে। মান তার জায়গাতেই লেখা — এই ছোট বদলটাই f-string-কে সবার প্রিয় করেছে।

প্র ০২Python 2-তে দুটি ফাংশন ছিল — input() আর raw_input(). Python 3 কেন একটি সরিয়ে অন্যটির নাম বদলে দিল?

Python 2-তে raw_input() আজকের input()-এর মতোই কাজ করত — একটি লাইন পড়ে str হিসেবে দিত। কিন্তু সেখানে input() নামে আরেকটি ফাংশন ছিল, যা আসলে ছিল eval(raw_input()): পড়া লেখাটিকে Python expression হিসেবে চালিয়ে তার ফল দিত। ব্যবহারকারী 25 লিখলে আসত int 25, 2.5 লিখলে float — শুরুতে সুবিধাজনক মনে হতো।

সমস্যা ছিল তিন রকম। প্রথমত, বিভ্রান্তি: নাম জিজ্ঞেস করলে ব্যবহারকারী Rafi লিখলেন, আর প্রোগ্রাম NameError: name 'Rafi' is not defined দিয়ে মরে গেল — কারণ Rafi কোট ছাড়া একটি variable-এর নাম হিসেবে পড়া হয়েছে। নতুনরা বুঝতেই পারতেন না কেন সবচেয়ে স্বাভাবিক নামের ফাংশনটি এমন অদ্ভুত আচরণ করছে। দ্বিতীয়ত, নিরাপত্তা: ব্যবহারকারী সংখ্যার বদলে যেকোনো expression লিখে প্রোগ্রামের ক্ষমতায় যা খুশি করাতে পারতেন — ফাইল মুছে ফেলা, অন্য প্রোগ্রাম চালানো। সবচেয়ে সহজ, সবচেয়ে বেশি শেখানো ফাংশনটিই ছিল সবচেয়ে বিপজ্জনক। তৃতীয়ত, নকশার নীতি: যে ফাংশন বাইরে থেকে লেখা আনে, তার কাজ শুধু লেখা আনা; সেটাকে কী বানানো হবে, সে সিদ্ধান্ত প্রোগ্রামারের।

২০০৬ সালে Andre Roberge — যিনি নতুনদের প্রোগ্রামিং শেখানোর টুল নিয়ে কাজ করতেন — PEP 3111 — “Simple input built-in in Python 3000” প্রস্তাব করেন। প্রথমে ভাবনা ছিল Python 3-এ ইনপুটের built-in পুরোপুরি সরিয়ে দেওয়ার, কিন্তু শিক্ষকেরা যুক্তি দেন যে নতুনদের প্রথম দিনের প্রোগ্রামের জন্য একটি সহজ ইনপুট ফাংশন অপরিহার্য। শেষ সিদ্ধান্ত: raw_input()-এর নাম হবে input(), আর পুরোনো eval-করা input() বাদ। ২০০৮ সালে Python 3.0-এর সাথে এটি কার্যকর হয়। Python 2-থেকে-3 কোড বদলানোর টুল 2to3 স্বয়ংক্রিয়ভাবে raw_input-কে input আর পুরোনো input(...)-কে eval(input(...)) বানিয়ে দিত — যাতে আচরণ হুবহু থাকে, আর ঝুঁকিটা চোখের সামনে দৃশ্যমান হয়।

আজকের AI কখনো কখনো এখনো raw_input() লেখে, বা “সুবিধার জন্য” eval(input()) প্রস্তাব করে — দুটোই সেই পুরোনো যুগের ছাপ। প্রথমটি অন্তত সাথে সাথে NameError দেয়; দ্বিতীয়টি চুপচাপ চলে, তাই বেশি বিপজ্জনক।

মূল উপলব্ধি: “সুবিধাজনক” আর “নিরাপদ” প্রায়ই উল্টো দিকে টানে। ভালো API সবচেয়ে সহজ পথটাকেই নিরাপদ পথ বানায় — Python 3 ঠিক তাই করেছে: input() শুধু লেখা দেয়, বিপজ্জনক কাজ করতে হলে আপনাকে জেনেশুনে eval লিখতে হবে।

প্র ০৩:>10,.2f-এর মতো format spec mini-language কোথা থেকে এলো? আর একই : দিয়ে তারিখ সাজানো যায় কীভাবে?

২০০৬ সালে Talin ছদ্মনামে পরিচিত একজন ডেভেলপার লেখেন PEP 3101 — “Advanced String Formatting”. তিনি %-formatting-এর কয়েকটি দুর্বলতা চিহ্নিত করেন: এটি একটি অপারেটর, তাই মান একটি tuple হলে অদ্ভুত error হয়; নাম ধরে মান বসানো কষ্টকর; আর সবচেয়ে বড় কথা, নতুন কোনো type (যেমন তারিখ বা দশমিক মুদ্রা) নিজের মতো করে সাজানোর নিয়ম যোগ করতে পারে না। PEP 3101-এর প্রস্তাব ছিল str.format() method, { } placeholder, আর কোলনের পরে একটি ছোট্ট, সুনির্দিষ্ট “format specification mini-language”, যা Python 3.0 আর 2.6-এ (২০০৮) আসে।

Mini-language-এর অনেক অংশ চেনা লাগবে যদি C-র printf দেখে থাকেন: width, .precision, আর d, f, e, x-এর মতো type অক্ষর printf-এর ঐতিহ্য থেকে এসেছে। নতুন যোগ হলো স্পষ্ট alignment চিহ্ন (< > ^), যেকোনো অক্ষর দিয়ে ভরাট, আর পরে আরও দুটি PEP: PEP 378 (Python 3.1 ও 2.7) আনে হাজারের কমা ,, আর PEP 515 (Python 3.6) আনে _ দিয়ে অঙ্ক ভাগ করা — সংখ্যার লেখায় (1_000_000) এবং format spec-এ দুই জায়গাতেই।

সবচেয়ে শক্তিশালী নকশা-সিদ্ধান্তটি কিন্তু চোখে পড়ে না: কোলনের পরের অংশের মানে ঠিক করে মানটির type নিজে। Python প্রতিটি object-এর __format__ method ডাকে, আর spec-টি তার হাতে তুলে দেয়। int আর float স্ট্যান্ডার্ড mini-language বোঝে; কিন্তু datetime object তার নিজস্ব ভাষা বোঝে — f"{today:%d-%m-%Y}" লিখলে আসে 17-10-2026-এর মতো তারিখ (পর্ব ২৭)। Decimal নিজের মতো দশমিক সামলায়। আপনি নিজে class বানালে (পর্ব ৩০) সেটিকেও নিজস্ব spec শেখাতে পারবেন। অর্থাৎ mini-language আসলে একটি চুক্তি: সাধারণ সংখ্যার জন্য একটি মানক ভাষা, আর যে type চায় সে নিজের ভাষা আনতে পারে।

এর একটি সীমা এই পর্বেই দেখেছেন: লাখ-কোটির গ্রুপিং মানক ভাষায় নেই। locale-নির্ভর n type দিয়ে কিছুটা পাওয়া যায়, কিন্তু সেটা নির্ভর করে চলমান কম্পিউটারের সেটিংসের ওপর — একই কোড দুই মেশিনে দুই রকম ফল দিতে পারে, যা রিপোর্টের জন্য অগ্রহণযোগ্য। তাই নিজস্ব ফাংশনই ভরসা।

মূল উপলব্ধি: ভালো এক্সটেনশন-নকশা কেন্দ্রীয় নিয়ম ছোট রাখে আর বাকিটা প্রতিটি অংশকে নিজে ঠিক করতে দেয়। __format__-এর মাধ্যমে Python ঠিক তাই করেছে — একটি :, অসংখ্য type-এর অসংখ্য ভাষা।

প্র ০৪eval() তো শুধু একটি expression হিসাব করে — ব্যবহারকারীর লেখায় এটা চালানো এত বিপজ্জনক কেন? আর “সাবধানে” ব্যবহার করলে কি চলে?

eval() একটি লেখাকে Python কোড হিসেবে পার্স করে, compile করে আর চালায় — আপনার প্রোগ্রামের ভেতরে, আপনার প্রোগ্রামের সব অনুমতি নিয়ে। “শুধু expression” শুনতে নিরীহ লাগে, কিন্তু Python-এ expression দিয়ে অনেক কিছু করা যায়: ফাংশন ডাকা যায়, module import করা যায় (__import__ একটি সাধারণ built-in ফাংশন), আর সেই module দিয়ে ফাইল খোলা, মোছা বা অন্য প্রোগ্রাম চালানো যায়। অর্থাৎ যে লেখা eval-এ যায়, তার লেখক কার্যত আপনার প্রোগ্রামের কোড লিখছেন।

এটি একটি বড় পরিবারের সদস্য, যাকে বলে injection — ডেটা হিসেবে আসা লেখা ভুল করে নির্দেশ হিসেবে পড়া। ডেটাবেসে SQL injection (ব্যবহারকারীর লেখা SQL কোয়েরির ভেতরে সরাসরি জুড়ে দেওয়া), ওয়েবে cross-site scripting (ব্যবহারকারীর লেখা HTML হিসেবে পেজে বসানো), শেল কমান্ডে command injection — সবগুলোর মূল কারণ এক: ডেটা আর কোডের সীমানা মুছে যাওয়া। OWASP-এর মতো নিরাপত্তা-সংস্থাগুলোর তালিকায় injection বহু বছর ধরে সবচেয়ে গুরুতর ঝুঁকিগুলোর মধ্যে থাকে।

“সাবধানে” ব্যবহারের চেষ্টা প্রায় সবসময় ব্যর্থ হয়েছে। লোকে eval(text, {"__builtins__": {}}) লিখে built-in সরিয়ে দেন, ভাবেন এখন নিরাপদ। কিন্তু Python-এ প্রতিটি object থেকে তার class-এ, সেখান থেকে অন্য class-এ যাওয়ার পথ আছে; অভিজ্ঞ আক্রমণকারীরা এই পথ ধরে আবার বিপজ্জনক ক্ষমতায় পৌঁছানোর কৌশল বহুবার দেখিয়েছেন। Python-এর ডকুমেন্টেশনও স্পষ্ট সতর্ক করে যে অবিশ্বস্ত লেখায় eval নিরাপদ করার কোনো নির্ভরযোগ্য উপায় নেই। আর একটি সূক্ষ্ম আক্রমণ আছে যা কোনো ফাইল ছোঁয় না: 9**9**9-এর মতো একটি নিরীহ-দেখতে হিসাব প্রোগ্রামকে দীর্ঘ সময় আটকে রাখতে পারে।

বিকল্পগুলো সহজ। সংখ্যা চাইলে int() বা float() — এরা শুধু সংখ্যা চেনে, অন্য কিছু পেলে ValueError. list বা dictionary-এর মতো literal পড়তে চাইলে ast.literal_eval(), যা শুধু literal (সংখ্যা, string, list, tuple, dict, set, True/False/None) গ্রহণ করে, কোনো ফাংশন ডাক নয়। সংরক্ষিত ডেটার জন্য JSON (পর্ব ২৬)। আর ক্যালকুলেটরের মতো সত্যিই হিসাব দরকার হলে নিজের ছোট parser — যা ঠিক যতটুকু অনুমতি দেওয়ার, ততটুকুই দেয়।

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

প্র ০৫Python 3.14-এ নতুন এসেছে t-string (t"..."). f-string থাকতে এটা কেন দরকার হলো, আর এটা কি f-string-এর জায়গা নেবে?

f-string-এর একটি জন্মগত বৈশিষ্ট্য আছে, যা কখনো কখনো দুর্বলতা: এটি সাথে সাথে একটি সাধারণ str বানিয়ে ফেলে। মান কোথায় বসেছিল, কোনটা আপনার লেখা আর কোনটা ব্যবহারকারীর দেওয়া — সেই তথ্য হারিয়ে যায়। ধরুন আপনি লিখলেন f"SELECT * FROM users WHERE name = '{name}'" আর name এসেছে ওয়েব-ফর্ম থেকে। ডেটাবেস লাইব্রেরির হাতে পৌঁছায় একটি তৈরি লেখা; সে আর জানতে পারে না কোন অংশটি বিপজ্জনক হতে পারে, তাই নিরাপদভাবে escape করার সুযোগও পায় না। এটাই SQL injection-এর ক্লাসিক রেসিপি, আর HTML তৈরিতে একই সমস্যা।

PEP 750 — “Template Strings” এর সমাধান হিসেবে গৃহীত হয় ২০২৫ সালে, আর Python 3.14-এ (অক্টোবর ২০২৫) যুক্ত হয়। এর লেখকদের মধ্যে আছেন Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, Lysandros Nikolaou ও Dave Peck. ধারণাটি নতুন নয় — Nick Coghlan-এর আগের প্রস্তাব PEP 501 আর JavaScript-এর “tagged template” থেকে অনুপ্রেরণা নেওয়া। ব্যাকরণ f-string-এর মতোই, শুধু f-এর জায়গায় t: t"Hello {name}". কিন্তু ফল একটি str নয় — string.templatelib module-এর একটি Template object. এর ভেতরে আলাদা করে রাখা থাকে স্থির লেখার টুকরোগুলো (.strings) আর বসানো মানগুলো (.interpolations; প্রতিটিতে মান, মূল expression-এর লেখা, conversion আর format spec).

এর ফলে একটি লাইব্রেরি টেমপ্লেট হাতে পেয়ে জানতে পারে কোন অংশ প্রোগ্রামারের লেখা (বিশ্বাসযোগ্য) আর কোন অংশ বাইরে থেকে আসা মান (সন্দেহজনক)। HTML লাইব্রেরি মানগুলোকে escape করে বসাতে পারে, SQL লাইব্রেরি সেগুলোকে আলাদা parameter হিসেবে ডেটাবেসে পাঠাতে পারে, logging লাইব্রেরি গঠনসহ সংরক্ষণ করতে পারে। গুরুত্বপূর্ণ কথা: Template নিজে নিজে কোনো চূড়ান্ত লেখায় রূপ নেয় না, আর str() দিলেও f-string-এর মতো জোড়া-লাগানো লেখা আসে না। তাকে কীভাবে লেখায় পরিণত করা হবে, তা ঠিক করে যে কোড তাকে প্রক্রিয়া করে। তাই t-string সাধারণ ব্যবহারকারীর চেয়ে লাইব্রেরি-নির্মাতাদের জন্য বেশি; ২০২৬ সালেও বেশিরভাগ জনপ্রিয় লাইব্রেরি এর পূর্ণ সমর্থন যোগ করার পথে।

তাহলে কি f-string বিদায় নেবে? না। পর্দায় একটি হিসাবের ফল দেখানো, রিপোর্টের সারি বানানো — এসব কাজে মান আপনারই হাতে, escape করার কিছু নেই; f-string-ই সবচেয়ে সরল ও সঠিক হাতিয়ার। t-string-এর জায়গা সেখানে, যেখানে লেখাটি অন্য কোনো ভাষায় (SQL, HTML, শেল কমান্ড) ঢুকবে আর ভেতরে বাইরের মান আছে।

মূল উপলব্ধি: তথ্য হারিয়ে গেলে নিরাপত্তাও হারায়। t-string-এর মূল শিক্ষা হলো, “কোনটা নির্দেশ আর কোনটা ডেটা” — এই সীমানা যত দেরিতে মুছবেন, তত নিরাপদ।

অনুশীলন

প্রথম তিনটিতে কোড লিখতে হবে না — আগে মাথায় উত্তর ঠিক করুন, তারপর নিজের কম্পিউটারে চালিয়ে মিলিয়ে দেখুন।

  1. ইনপুটের ফাঁদ: প্রতিটির ক্ষেত্রে ব্যবহারকারী 7 লিখে Enter দিলেন। আউটপুট কী হবে? (ক) print(input() * 3) (খ) print(int(input()) * 3) (গ) print(float(input()) / 2) (ঘ) print(input() + 3)

    (ক) 777 — str-কে সংখ্যা দিয়ে গুণ করলে লেখাটি ততবার পুনরাবৃত্তি হয়। (খ) 21 — রূপান্তরের পরে আসল গুণ। (গ) 3.5. (ঘ) TypeError: can only concatenate str (not "int") to str — str আর int সরাসরি যোগ হয় না। (ক) সবচেয়ে বিপজ্জনক: কোনো error নেই, শুধু ভুল উত্তর।

  2. Format spec পড়ুন: n = 42 আর r = 0.0815 ধরে প্রতিটির ফল [ ]-সহ লিখুন — (ক) f"[{n:>6}]" (খ) f"[{n:06d}]" (গ) f"[{n:-^8}]" (ঘ) f"[{r:.1%}]" (ঙ) f"[{n * 1000:,}]"

    (ক) [ 42] — ৬ ঘর, ডানে। (খ) [000042] — শূন্য দিয়ে ভরা। (গ) [---42---] — - দিয়ে ভরাট, মাঝে, ৮ ঘর। (ঘ) [8.2%] — ১০০ দিয়ে গুণ করে এক দশমিক (৮.১৫ → ৮.২)। (ঙ) [42,000]. (গ)-তে খেয়াল করুন: প্রথম - ভরাটের অক্ষর, কারণ ঠিক পরেই alignment চিহ্ন ^ আছে।

  3. বাংলা কলামের পরীক্ষা: নিজের কম্পিউটারে এই দুই লাইন চালান — print(f"{'আম':<8}|") আর print(f"{'কাঁঠাল':<8}|"). | চিহ্ন দুটি কি একই কলামে এলো? দুটি শব্দের len() বের করে কারণ ব্যাখ্যা করুন।

    len("আম") = ২ (আ, ম), তাই ৬টি space বসে। len("কাঁঠাল") = ৬ (ক, া, ঁ, ঠ, া, ল), তাই মাত্র ২টি space. কিন্তু পর্দায় “কাঁঠাল” দেখতে মোটামুটি চার ঘরের মতো, কারণ কার-চিহ্ন আর চন্দ্রবিন্দু আলাদা ঘর নেয় না। ফলে দ্বিতীয় লাইনের | বাঁয়ে সরে আসে। আপনার টার্মিনাল আর ফন্টের ওপর নির্ভর করে অমিলের পরিমাণ একটু আলাদা হতে পারে — ঠিক এজন্যই বাংলা লেবেল টেবিলের শেষ কলামে রাখা নিরাপদ।

  4. নিজের প্রোগ্রাম — টিপ ও ভাগাভাগি: বন্ধুরা মিলে রেস্তোরাঁয় খেয়েছেন। প্রোগ্রামটি এক লাইনে তিনটি মান পড়বে: মোট বিল (দশমিক হতে পারে), টিপের শতাংশ আর কতজন। তারপর টিপ, টিপসহ মোট আর জনপ্রতি ভাগ — তিনটিই ,.2f দিয়ে, ডানে সাজানো একটি ছোট টেবিলে দেখাবে।

    একটি নমুনা (একমাত্র সঠিক উত্তর নয়):

    bill_text, tip_text, people_text = input().split()
    bill = float(bill_text)
    tip_pct = float(tip_text)
    people = int(people_text)
    
    tip = bill * tip_pct / 100
    total = bill + tip
    print(f"{'Tip':<12}{tip:>12,.2f}")
    print(f"{'Total':<12}{total:>12,.2f}")
    print(f"{'Per person':<12}{total / people:>12,.2f}")
    
    # ইনপুট: 2450 10 4
    # Tip               245.00
    # Total           2,695.00
    # Per person        673.75

    খেয়াল করুন, তিনটি মানের type আলাদা (দুটি float, একটি int), তাই এখানে map ব্যবহার না করে আলাদা করে রূপান্তর করা হয়েছে। যাচাই করুন: ২৪৫০-এর ১০% = ২৪৫, মোট ২৬৯৫, চারজনে ভাগ ৬৭৩.৭৫ — মেলে? আর 0 জন দিলে কী হয়? (ZeroDivisionError — এটি সামলানো শিখবেন পর্ব ১১ ও ২৪-এ।)

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

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

ক্রমPython-এর format spec-এ (যেমন >10,.2f) অংশগুলো যে ক্রমে লিখতে হয়, সেই ক্রমে সাজান (▲▼ চেপে সরান)

  1. alignment — যেমন >
  2. width — যেমন 10
  3. হাজারের কমা — ,
  4. precision — যেমন .2
  5. type — যেমন f

Python 3-এ ব্যবহারকারী কীবোর্ডে 42 লিখে Enter চাপলে input() কী ফেরত দেয়?

  • int 42
  • str '42'
  • str '42\n'
  • float 42.0

Python-এ int("২৫") + int("25.5") চালালে কী ঘটবে?

  • 50
  • 50.5
  • ValueError — '25.5' পূর্ণসংখ্যা নয়
  • TypeError — বাংলা অঙ্ক চেনে না

Python-এ price = 1234.5 হলে f"[{price:>10,.2f}]"-এর ফল কোনটি?

  • [ 1,234.50]
  • [1,234.50 ]
  • [ 1234.5]
  • [1,234.5]

ব্যবহারকারী এক লাইনে 3 5 লিখলে Python-এ কোন লাইনটি a = 3 আর b = 5 (দুটিই int) বানাবে?

  • a, b = input()
  • a, b = input().split()
  • a, b = int(input())
  • a, b = map(int, input().split())

Python 3.8 বা নতুন version-এ total = 150 হলে print(f"{total=}") কী ছাপে?

  • 150
  • total=150
  • SyntaxError
  • True

ব্যবহারকারীর ইনপুট থেকে সংখ্যা পড়তে eval(input()) ব্যবহার করা উচিত নয় কেন?

  • এটি int(input())-এর চেয়ে অনেক ধীর
  • Python 3-এ eval সরিয়ে দেওয়া হয়েছে
  • এটি ব্যবহারকারীর লেখা কোড হিসেবে চালায় — যেকোনো ক্ষতিকর কাজ করানো সম্ভব
  • এটি দশমিক সংখ্যা পড়তে পারে না

শব্দকোষ

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

মনে রাখুন

  1. প্রোগ্রাম আর বাইরের জগতের মধ্যে চলে শুধু লেখা — stdin দিয়ে ঢোকে, stdout দিয়ে বের হয়। এই কোর্সের কোড-বক্সে stdin আগে থেকে দেওয়া থাকে, তাই উত্তরগুলো আউটপুটে দেখা যায় না।
  2. input() সবসময় str দেয়; সংখ্যার জন্য int()/float(). ভুল লেখায় ValueError; আপাতত .isdigit(), পরে try/except.
  3. এক লাইনে কয়েকটি সংখ্যা: a, b = map(int, input().split()) — ভাঙো, বদলাও, ভাগ করো।
  4. f-string-এ { }-এর ভেতরে যেকোনো expression; :-এর পরে spec: [ভরাট][দিক][চওড়া][,][.দশমিক][type]; ডিবাগিংয়ে {x=}.
  5. বাংলা লেখায় width গোনা হয় code point দিয়ে, তাই কলাম বাঁকা হয়। কখনো eval(input()) নয়; error-বার্তা stderr-এ, দেরিতে আসা লেখার জন্য flush=True.

পরের পর্বে: এই পর্বে বারবার বলেছি “শর্তের ওপর ভিত্তি করে আলাদা কাজ পরের পর্বে।” এবার সেই পালা — if/elif/else দিয়ে প্রোগ্রাম সিদ্ধান্ত নিতে শিখবে: BMI দেখে “স্বাভাবিক” না “বেশি”, বিদ্যুৎ বিলের ধাপ, আর আধুনিক match.

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

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