مسیر
/
Python
Python
/
برنامه‌ی درسی
/
قالب‌بندی رشته
قا

قالب‌بندی رشته در Python

1 تمرین

درباره‌ی قالب‌بندی رشته

قالب‌بندی رشته در Python

قالب‌بندی رشته فرایند تبدیل مقادیر به رشته و جای‌گذاری آن‌ها در یک قالب رشته‌ای است. Zen of Python معتقد است که باید «یک روش واضح برای انجام یک کار در Python» وجود داشته باشد. اما وقتی صحبت از قالب‌بندی رشته به میان می‌آید، همه‌چیز کمی... کمتر ذن‌گونه است.

ممکن است غافلگیرکننده باشد وقتی بفهمید چهار روش اصلی برای انجام قالب‌بندی رشته در Python وجود دارد، که هر کدام برای موقعیتی متفاوت است. بخشی از این موضوع به تاریخ طولانی Python برمی‌گردد و بخشی دیگر به ملاحظاتی مانند بین‌المللی‌سازی یا پاک‌سازی ورودی. ما با جدیدترین افزوده‌ها به جعبه‌ابزار قالب‌بندی رشته شروع می‌کنیم و به عقب برمی‌گردیم تا به قالب‌بندی رشته‌ی «سبک قدیمی» یا «سبک printf()» برسیم.

درون‌یابی رشته‌ی تحت‌اللفظی: f-string

f-stringها (کوتاه‌شده‌ی «formatted strings») یا درون‌یابی رشته‌ی تحت‌اللفظی که در Python 3.6 معرفی شدند، روشی برای ارزیابی (و قالب‌بندی) سریع و کارآمد عبارت‌ها و رشته‌ها به نوع str هستند، با استفاده از پیشوند f (یا F) پیش از گیومه‌ها (مانند این: 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!'

عبارت‌ها داخل {}: هر عبارت معتبر Python را می‌توان داخل آکولادها ارزیابی کرد. توجه کنید که استفاده از گیومه‌ی دوتایی داخل یک 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'
Caution

درون‌یابی رشته را نمی‌توان همراه با GNU gettext API برای بین‌المللی‌سازی (I18N) و بومی‌سازی (L10N) به کار برد، بنابراین توصیه می‌شود در هر سناریوی ترجمه‌ای که «بسته‌بندی رشته» مطرح است، به‌جای 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() در Python از طیف وسیعی از تعیین‌کننده‌های قالب زبان کوچک پشتیبانی می‌کند که می‌توان از آن‌ها برای ترازبندی متن، کوتاه کردن اعداد اعشاری و موارد بیشتر استفاده کرد.

الگوی کامل تعیین‌کننده‌ی قالب‌بندی {[<name>][!<conversion>][:<format_specifier>]} است:

  • <name> می‌تواند یک جای‌نگه‌دار نام‌دار، یک عدد، یا خالی باشد.
  • !<conversion> اختیاری است و باید یکی از این سه تبدیل باشد: !s برای str()، !r برای repr() یا !a برای ascii(). به‌طور پیش‌فرض، از 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()

استفاده از عملگر % برای قالب‌بندی، قدیمی‌ترین روش قالب‌بندی رشته در Python است. این روش از زبان C می‌آید و امکان استفاده از آرگومان‌های مکانی را برای ساخت یک str فراهم می‌کند.

این روش با f-stringها و str.format() جایگزین شده است، به همین دلیل لقب قالب‌بندی %، «سبک قدیمی» است. این روش هنوز در 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() (که نباید با t-strings در Python 3.14 اشتباه گرفته شود) کلاسی از ماژول string است (برخلاف نوع داخلی str) که بخشی از کتابخانه‌ی استاندارد Python است، اما برای استفاده باید وارد شود. رشته‌های قالب از جایگزینی مبتنی بر $ پشتیبانی می‌کنند و بسیار ساده‌تر و کم‌توان‌تر از سایر گزینه‌های ذکرشده در اینجا هستند. با این حال، می‌توانند برای بین‌المللی‌سازی پیچیده یا پاک‌سازی ورودی‌های بیرونی بسیار مفید باشند.

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 را می‌توانید در مستندات Python پیدا کنید.

چگونه انتخاب کنید از کدام روش قالب‌بندی استفاده کنید؟

با این همه گزینه و زبان کوچک، چطور تصمیم می‌گیرید هنگام قالب‌بندی رشته‌های Python سراغ کدام بروید؟ چند راهنمای سریع در اینجا آمده است:

  1. f-stringها جدیدترین و خواناترین هستند. اگر نیازی به بین‌المللی‌سازی ندارید، باید روش ترجیحی برای Python 3.6+ باشند.

  2. str.format() همه‌کاره، قدرتمند و سازگار با هم gnu gettext و هم بیشتر نسخه‌های Python است.

  3. اگر سادگی، ایمنی و/یا بین‌المللی‌سازی سنگین چیزی است که می‌خواهید، می‌توان از string.Template() برای کاهش ریسک‌ها هنگام نیاز به پردازش ورودی‌های کاربران، و برای بسته‌بندی رشته‌های ترجمه استفاده کرد.

  4. عملگر % برای کدهای جدید عموماً منسوخ در نظر گرفته می‌شود، هرچند هنوز در Python مدرن کار می‌کند. بیشتر باید برای سازگاری با پایگاه‌های کد قدیمی استفاده شود. قالب‌بندی % بیش از روش‌های دیگر مستعد خطاست (و عملکرد کمتری دارد) و می‌تواند به مشکلاتی در نمایش نویسه‌های غیر ASCII منجر شود. اگر قصد استفاده از آن را دارید، برای جزئیات پشتیبانی، توزیع خاص Python خود را بررسی کنید.

اگر می‌خواهید بیشتر پیش بروید، all about formatting و Python String Formatting Best Practices نقاط شروع خوبی هستند.

ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود