স্ট্রিং ফরম্যাটিং হলো মানগুলোকে স্ট্রিংয়ে রূপান্তর করে একটি স্ট্রিং টেমপ্লেটে বসানোর প্রক্রিয়া।
Zen of Python-এ বলা হয়েছে, "পাইথনে কোনো কাজ করার একটি স্পষ্ট উপায়" থাকা উচিত।
কিন্তু স্ট্রিং ফরম্যাটিংয়ের ক্ষেত্রে বিষয়টা একটু... কম জেন।
জেনে অবাক হবেন যে পাইথনে স্ট্রিং ফরম্যাট করার চারটি প্রধান উপায় আছে, প্রতিটি ভিন্ন পরিস্থিতির জন্য।
এর কিছুটা পাইথনের দীর্ঘ ইতিহাসের কারণে, আর কিছুটা ইন্টারন্যাশনালাইজেশন বা ইনপুট স্যানিটাইজেশনের মতো বিবেচনার কারণে।
আমরা স্ট্রিং ফরম্যাটিং টুলবক্সের সবচেয়ে সাম্প্রতিক সংযোজনগুলো দিয়ে শুরু করব এবং পিছনে হেঁটে "পুরনো স্টাইল" বা "printf() স্টাইল" স্ট্রিং ফরম্যাটিংয়ে পৌঁছাব।
f-string
Python 3.6-এ চালু হওয়া f-stringগুলো (সংক্ষেপে "formatted strings") বা লিটারাল স্ট্রিং ইন্টারপোলেশন, হলো কোট চিহ্নের আগে f (বা F) প্রিফিক্স ব্যবহার করে এক্সপ্রেশন ও স্ট্রিংগুলোকে দ্রুত ও দক্ষভাবে একটি str টাইপে ইভ্যালুয়েট (এবং ফরম্যাট) করার একটি উপায় (এভাবে: f'{object}')।
f-string সব ধরনের আবদ্ধ স্ট্রিং টাইপের সাথে ব্যবহার করা যায়, যেমন এক-লাইনের (' বা ") এবং বহু-লাইনের (''' বা """)।
{}-এর ভেতরে রাখা যেকোনো ভ্যারিয়েবল, এক্সপ্রেশন বা অন্য টাইপ প্রথমে ইভ্যালুয়েট করা হয়, তারপর একটি str-এ রূপান্তর করা হয়, এবং সবশেষে বন্ধনীর বাইরের যেকোনো str-এর সাথে যুক্ত করা হয়।
এই উদাহরণে আমরা একটি বাক্যে দুটি ভ্যারিয়েবলের মান বসাই (একটি str আর একটি float):
>>> name = 'eighth'
>>> value = 1/8
# The f-string, using the two values.
# The .2f format code truncates, so the value displays as 0.12.
>>> f'An {name} is approximately {value:.2f}.'
'An eighth is approximately 0.12.'
ইভ্যালুয়েট করা এক্সপ্রেশন প্রায় যেকোনো কিছুই হতে পারে।
ইভ্যালুয়েট করা যায় এমন (বিস্তৃত পরিসরের) সম্ভাবনার কিছু নমুনা: str, সংখ্যা, ভ্যারিয়েবল, গাণিতিক এক্সপ্রেশন, কন্ডিশনাল এক্সপ্রেশন, বিল্ট-ইন টাইপ, স্লাইস, ফাংশন, ল্যাম্বডা, কম্প্রিহেনশন, কিংবা __str__ বা __repr__ মেথড ডিফাইন করা যেকোনো অবজেক্ট।
সহজ থেকে জটিলের দিকে যাওয়া যাক:
একটি ভ্যারিয়েবল বসানো: f-string-এর সবচেয়ে সহজ ব্যবহার হলো সরাসরি স্ট্রিংয়ের ভেতরে একটি ভ্যারিয়েবল রাখা।
# Assigning a variable.
>>> name = "World"
# Inserting that variable.
>>> f'Hello, {name}!'
'Hello, World!'
{}-এর ভেতরে এক্সপ্রেশন: বন্ধনীর ভেতরে যেকোনো বৈধ পাইথন এক্সপ্রেশন ইভ্যালুয়েট করা যায়।
খেয়াল করুন, একটি সিঙ্গেল-কোটেড f-string-এর ভেতরে ডাবল কোট ব্যবহার করলে (বা উল্টোটা) এস্কেপ সিকোয়েন্সের দরকার পড়ে না:
# A dictionary of key:value pairs.
>>> waves = {'water': 1, 'light': 3, 'sound': 5}
# Inserting the whole dict.
>>> f'Wave ranks: {waves}'
"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"
# An expression can be evaluated inline:
>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
"Tenfold the value of 'light' is 30."
# A method call can also be evaluated inline:
>>> f'{"hello world!".title()} is a classic greeting.'
'Hello World! is a classic greeting.'
# An f-string can be nested inside another f-string:
>>> f"{f'hello world!'.title()} is a classic greeting."
'Hello World! is a classic greeting.'
আউটপুট ফরম্যাটিং: ফরম্যাট স্পেসিফিকেশন মিনি-ভাষা ব্যবহার করে অ্যালাইনমেন্ট, সংখ্যার প্রেসিশন এবং আরও অনেক কিছু নিয়ন্ত্রণ করা যায়।
ফরম্যাট স্পেসিফিকেশন মানের পরে বসে, একটি : দিয়ে আলাদা করা হয়।
# Right-align a value to ten characters, and round it to 3 decimal places.
>>> value = 1 / 7
>>> f'One seventh is {value:10.3f}.'
'One seventh is 0.143.'
# A format specification can be set using variables as well.
>>> padding = 10
>>> precision = 3
>>> f'One seventh is {value:{padding}.{precision}f}.'
'One seventh is 0.143.'
সবকিছু একসাথে জোড়া লাগানো: ভ্যারিয়েবল, এক্সপ্রেশন, ফাংশন কল এবং আউটপুট ফরম্যাটিং:
>>> precision = 3
>>> f"{30e8 * 111_000:6.{precision}e}"
'3.330e+14'
>>> verb = 'meet'
>>> the_end = ['end', 'of', 'transmission']
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'
# Did you notice the escaped single-quotes in the previous example?
# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
>>> f"Have a nice day. {the_end}"
"Have a nice day. ['end', 'of', 'transmission']"
দুটি প্রধান সীমাবদ্ধতার কথা মনে রাখা দরকার।
f-string এক্সপ্রেশন খালি হতে পারে না।
এছাড়া Python 3.12-এর আগে এগুলোতে কমেন্ট থাকতে পারত না।
>>> f"An empty expression will error: {}"
SyntaxError: f-string: empty expression not allowed
>>> word = 'word'
>>> f"""A comment in a triple quoted f-string: {
word # I chose a nice variable
}"""
'A comment in a triple quoted f-string: word'
স্ট্রিং ইন্টারপোলেশন ইন্টারন্যাশনালাইজেশন (I18N) ও লোকালাইজেশনের (L10N) জন্য GNU gettext API-এর সাথে একসাথে ব্যবহার করা যায় না, তাই যেকোনো "string wrapping" অনুবাদ পরিস্থিতিতে f-string-এর বদলে নিচে বর্ণিত string.Template ক্লাস বা str.format() মেথড ব্যবহার করার পরামর্শ দেওয়া হয়।
আরও মনে রাখবেন, f-string-এর বন্ধনী {}-এর ভেতরে এক্সপ্রেশন ব্যবহার করা eval() বা exec() ব্যবহারের মতোই, তাই এটি খুব একটা নিরাপদ নয় এবং ব্যবহারকারীর ইনপুটের সাথে কখনোই ব্যবহার করা উচিত নয়।
str.format() মেথডstr.format() মেথড স্ট্রিংয়ের ভেতরের প্লেসহোল্ডারগুলোকে ফাংশনে আর্গুমেন্ট হিসেবে দেওয়া মান দিয়ে প্রতিস্থাপন করে।
প্লেসহোল্ডারগুলো নাম ({price}), সংখ্যা ({0} বা ইনডেক্স), কিংবা এমনকি খালি (পজিশনাল) প্লেসহোল্ডার {} দিয়ে চিহ্নিত করা হয়।
উদাহরণস্বরূপ:
# A named placeholder and a positional placeholder.
>>> 'My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder')
'My text: named placeholder and 12.'
f-string-এর মতোই, পাইথনের str.format() একগুচ্ছ মিনি ভাষা ফরম্যাট স্পেসিফায়ার সমর্থন করে, যা দিয়ে টেক্সট অ্যালাইন করা, ফ্লোট ছোট করা এবং আরও অনেক কিছু করা যায়।
সম্পূর্ণ ফরম্যাটিং স্পেসিফায়ার প্যাটার্নটি হলো {[<name>][!<conversion>][:<format_specifier>]}:
<name> একটি নামযুক্ত প্লেসহোল্ডার, একটি সংখ্যা, কিংবা খালি হতে পারে।!<conversion> ঐচ্ছিক এবং এই তিনটি কনভার্সনের একটি হওয়া উচিত: str()-এর জন্য !s, repr()-এর জন্য !r বা ascii()-এর জন্য !a।
ডিফল্টভাবে str() ব্যবহৃত হয়।:<format_specifier> ঐচ্ছিক এবং মানটি কীভাবে দেখানো হবে তা নিয়ন্ত্রণ করে।
সম্ভাব্য অপশন সম্পর্কে আরও তথ্য এখানে পাওয়া যাবে।একটি ডায়াক্রিটিক অক্ষরের জন্য কনভার্সনের উদাহরণ:
# Fills in the object at index zero, converted to a string.
>>> 'An e with an umlaut: {0!s}'.format('ë')
'An e with an umlaut: ë'
# Fills in the object at index zero, converted to a repr.
>>> 'An e with an umlaut (object representation): {0!r}'.format('ë')
"An e with an umlaut (object representation): 'ë'"
# Fills in the object at index zero, converted to ASCII.
>>> 'An e with an umlaut converted into ASCII: {0!a}'.format('ë')
"An e with an umlaut converted into ASCII: '\xeb'"
# Fills in the object in the first position,
# then fills in the object in the second position formatted as a repr.
>>> 'She said her name is not {} but {!r}.'.format('Chloe', 'Zoë')
"She said her name is not Chloe but 'Zoë'."
সাধারণ ফরম্যাট স্পেসিফায়ারের উদাহরণ:
# Integer and binary/hex representations of the same number.
>>> my_num = 42
>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
"42 in binary is 101010. In hex, it is 2a"
# Alignment: left (<), right (>), and center (^) using up to ten characters total.
>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
"[left ] [ right] [ center ]"
# Float precision and scientific notation (up to three decimal places).
>>> pi = 3.141592653589793
>>> f"Fixed: {pi:.3} Scientific: {pi:.3e}"
"Fixed: 3.142 Scientific: 3.142e+00"
# Thousands separator and percentage.
>>> balance = 1000
>>> rate = 0.0225
>>> f"Balance: ${balance:,.0f} Interest rate: {rate:.1%}"
"Balance: $1,000 Interest rate: 2.2%"
# Putting it all together:
>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
>>> print(header)
Item Qty Price
>>> for name, qty, price in items:
... print(f"{name:<12} {qty:>6} {price:>9.2f}")
Widget 1250 9.99
Gadget 37 24.50
Doohickey 4 149.00
আরও উদাহরণ এই ডকুমেন্টেশনের শেষে দেখানো হয়েছে।
% ফরম্যাটিং, বা printf() স্টাইল ফরম্যাটিংফরম্যাটিংয়ের জন্য % অপারেটর ব্যবহার করা পাইথনে স্ট্রিং ফরম্যাটিংয়ের সবচেয়ে পুরনো পদ্ধতি।
এটি C ভাষা থেকে এসেছে এবং পজিশনাল আর্গুমেন্ট ব্যবহার করে একটি str গঠনের সুযোগ দেয়।
এই পদ্ধতিটি f-string ও str.format() উভয়ের দ্বারা প্রতিস্থাপিত হয়েছে, তাই % ফরম্যাটিংয়ের ডাকনাম "Old Style"।
এটি এখনও Python 2 এবং/অথবা লিগ্যাসি কোডে পাওয়া যায়।
Python 3.x-এ এই পদ্ধতি কাজ করবে, তবে % ফরম্যাটিং সাধারণত এড়িয়ে চলা হয়, কারণ এটি ভুলপ্রবণ হতে পারে, কম দক্ষ, এতে কম অপশন থাকে এবং প্লেসহোল্ডার ও আর্গুমেন্টের যেকোনো অমিল একটি এক্সসেপশন ছুঁড়ে দিতে পারে।
% অপারেটর ব্যবহার printf()-এর মতো, তাই এটিকে মাঝে মাঝে _printf ফরম্যাটিং_ও বলা হয়।
# Assigning a variable.
>>> name = "Anna-conda"
# Building a string using %.
>>> "The snake's name is %s." % name
"The snake's name is Anna-conda."
উপরের উদাহরণে, % অপারেটর রানটাইমে %s প্লেসহোল্ডারটিকে name ভ্যারিয়েবল দিয়ে প্রতিস্থাপন করে।
আপনি যদি একটি স্ট্রিংয়ে একাধিক ভ্যারিয়েবল যোগ করতে চান, তাহলে %-এর পরে প্রতিটি প্লেসহোল্ডারের জন্য একটি করে অবজেক্ট ধারণ করা একটি টাপল দিতে হবে:
# Assigning variables.
>>> name = "Billy the Kid"
>>> fruit = "grapes"
# Building a string using %.
>>> "Surprisingly, %s's favorite snack was %s." %(name, fruit)
"Surprisingly, Billy the Kid's favorite snack was grapes."
string.Template() (Python 3.14-এর t-strings-এর সাথে গুলিয়ে ফেলবেন না) হলো string মডিউলের (বিল্ট-ইন str টাইপের বিপরীতে) একটি ক্লাস, যা পাইথন স্ট্যান্ডার্ড লাইব্রেরির অংশ, তবে ব্যবহারের জন্য ইমপোর্ট করতে হয়।
টেমপ্লেট স্ট্রিং $-ভিত্তিক প্রতিস্থাপন সমর্থন করে এবং এখানে উল্লিখিত অন্য অপশনগুলোর চেয়ে অনেক সহজ ও কম সক্ষম।
তবে জটিল ইন্টারন্যাশনালাইজেশন বা বাইরের ইনপুট স্যানিটাইজ করার জন্য এগুলো খুবই উপকারী হতে পারে।
অবিশ্বস্ত ব্যবহারকারীর ইনপুটের জন্য string.Template-কে বেশি নিরাপদ মনে করা হয়, কারণ এটি ইচ্ছামতো এক্সপ্রেশন ইভ্যালুয়েট করা বা অবজেক্টের অ্যাট্রিবিউট অ্যাক্সেস করা প্রতিরোধ করে, যা ফরম্যাট-স্ট্রিং ইনজেকশন আক্রমণ প্রশমিত করে।
>>> from string import Template
>>> name = "Anna-Conda"
# Creating a Template with placeholder text.
>>> template_string = Template("The snake called `$snake_name` has escaped!")
# Calling .substitute() to replace the placeholder with a value.
>>> template_string.substitute(snake_name=name)
'The snake called `Anna-Conda` has escaped!'
string.Template সম্পর্কে আরও তথ্য পাইথন ডকুমেন্টেশনে পাওয়া যাবে।
এইসব অপশন আর মিনি-ভাষা নিয়ে, পাইথন স্ট্রিং ফরম্যাট করার সময় কোনটি ব্যবহার করবেন তা কীভাবে ঠিক করবেন? এখানে কয়েকটি দ্রুত নির্দেশিকা:
f-stringগুলো সবচেয়ে নতুন এবং পড়তে সবচেয়ে সহজ।
আপনার যদি ইন্টারন্যাশনালাইজ করার দরকার না থাকে, তাহলে Python 3.6+ এর জন্য এটিই হওয়া উচিত পছন্দের পদ্ধতি।
str.format() বহুমুখী, শক্তিশালী এবং gnu gettext ও বেশিরভাগ পাইথন সংস্করণের সাথে সামঞ্জস্যপূর্ণ।
আপনার যদি সরলতা, নিরাপত্তা এবং/অথবা ভারী ইন্টারন্যাশনালাইজেশন দরকার হয়, তাহলে ব্যবহারকারীর ইনপুট সামলাতে এবং অনুবাদ স্ট্রিং র্যাপ করতে ঝুঁকি প্রশমিত করার জন্য string.Template() ব্যবহার করা যায়।
নতুন কোডের জন্য % অপারেটরকে সাধারণত ডেপ্রিকেটেড ধরা হয়, যদিও এটি আধুনিক পাইথনে এখনও কাজ করে।
এটি মূলত পুরনো কোডবেসের সাথে সামঞ্জস্যের জন্য ব্যবহার করা উচিত।
% ফরম্যাটিং অন্য পদ্ধতিগুলোর চেয়ে বেশি ভুলপ্রবণ (এবং কম কার্যকারিতা সম্পন্ন), আর এটি নন-ASCII অক্ষর প্রদর্শনে সমস্যা তৈরি করতে পারে।
ব্যবহার করার ইচ্ছা থাকলে সাপোর্টের বিস্তারিত জানতে আপনার নির্দিষ্ট পাইথন ডিস্ট্রিবিউশন দেখে নিন।
আরও এগোতে চাইলে, all about formatting এবং Python String Formatting Best Practices শুরু করার ভালো জায়গা।