Python 字符串格式:% vs 串联

Python string formatting: % vs concatenation

我正在开发一个应用程序,我在其中执行一些请求以获取对象 ID。在每一个之后,我调用一个方法 (get_actor_info()) 将此 id 作为参数传递(参见下面的代码)。

ACTOR_CACHE_KEY_PREFIX = 'actor_'

def get_actor_info(actor_id):
    cache_key = ACTOR_CACHE_KEY_PREFIX + str(actor_id)

如您所见,我将 actor_id 转换为 string 并将其与前缀连接。但是,我知道我可以通过多种其他方式(例如 .format()'%s%d')来实现,这导致了我的问题:就可读性而言,'%s%d' 会比字符串连接更好吗, 代码约定和效率?

谢谢

就性能而言,串联更好。在您的示例中,串联和替换都是可读的,但是当涉及到更复杂的模板时,替换在简单性和可读性竞赛中获胜。

例如,如果您有数据并且想在 html 中显示它,串联会让您头疼,而替换将简单易读。

这很容易成为一个 opinion-based 线程,但我发现在大多数情况下格式更易读,更易于维护。无需执行 "mental concatenation",更容易想象最终字符串的样子。例如,哪一个更具可读性?

errorString = "Exception occurred ({}) while executing '{}': {}".format(
    e.__class__.__name__, task.name, str(e)
)

或:

errorString = "Exception occurred (" + e.__class__.__name__
    + ") while executing '" + task.name + "': " + str(e)

至于用%还是.format(),我可以比较客观的回答:用.format()% 是 "old-style",根据 Python Documentation,它们可能很快就会被删除:

Since str.format() is quite new, a lot of Python code still uses the % operator. However, because this old style of formatting will eventually be removed from the language, str.format() should generally be used.

更高版本的文档已不再提及这一点,但尽管如此,.format() 是未来的方式;使用它!

连接速度更快,但这不是问题。以 first-line 为目标,让你的代码具有可读性和可维护性,然后优化你需要稍后优化的部分。过早的优化是万恶之源;)

Python 3.6 将引入另一个选项:

ACTOR_CACHE_KEY_PREFIX = 'actor_'

def get_actor_info(actor_id):
    cache_key = f'{ACTOR_CACHE_KEY_PREFIX}{actor_id}'

性能应该与 '{}{}'.format(ACTOR_CACHE_KEY_PREFIX, actor_id) 相当,但可以说更具可读性。

我猜想,如果要连接的所有项都是常量,则 python 可能会优化与 + 运算符的连接以提高性能。例如:

DB_PREFIX = 'prod_'
INDEX_PREFIX = 'index_'

CRM_IDX_PREFIX = DB_PREFIX + INDEX_PREFIX + 'crm_'

但大多数情况下,格式函数和运算符用于连接变量内容。例如:

crm_index_name = "{}_{}".format(CRM_IDX_PREFIX, index_id)

实际上,如果您使用 + 运算符像这样连接:

crm_index_name = CRM_IDX_PREFIX + '_' + str(index_id)

您正在以固定方式通过自定义代码定义格式。如果您使用带有命名引用的格式字符串,代码将更具可读性。例如:

crm_index_name = "{db_prefix}_{idx_prefix}_{mod_prefix}_{id}".format(
   db_prefix=CRM_IDX_PREFIX,
   idx_prefix=INDEX_PREFIX,
   mod_prefix='crm',
   id=index_id,
)

这样您就可以将格式定义为常量。例如:

IDX_FORMAT = "{db_prefix}_{idx_prefix}_{mod_prefix}_{id}"

crm_index_name = IDX_FORMAT.format(
   db_prefix=CRM_IDX_PREFIX,
   idx_prefix=INDEX_PREFIX,
   mod_prefix='crm',
   id=index_id,
)

并且这个结果更清楚,以防您将来需要更改格式。 例如,为了改变分隔符的顺序,你只需要改变 将字符串格式化为:

IDX_FORMAT = "{db_prefix}_{mod_prefix}_{idx_prefix}-{id}"

另外,为了调试,您可以将所有这些变量分配给一个字典,并将其作为关键字参数传递给格式函数:

idx_name_parts = {
   'db_prefix': CRM_IDX_PREFIX,
   'idx_prefix': INDEX_PREFIX,
   'mod_prefix': 'crm',
   'id': index_id,
}
crm_index_name = IDX_FORMAT.format(**idx_name_parts)

利用globals()函数我们还可以:

IDX_FORMAT = "{CRM_IDX_PREFIX}_{mod_prefix}_{INDEX_PREFIX}-{index_id}"

crm_index_name = IDX_FORMAT.format(mod_prefix = 'crm', **globals())

类似于 python3 的 formatted string literal:

crm_index_name = f"{CRM_IDX_PREFIX}_crm_{INDEX_PREFIX}-{index_id}"

我还看到 Internationalization 作为另一种使用上下文,其中格式化表达式比 + 运算符更有用。取以下代码:

message = "The account " + str(account_number) + " doesn't exist"

如果您将 gettext module 之类的翻译功能与 + 运算符一起使用,它将是:

message = _("The account ") + str(account_number) + _(" doesn't exist")

所以最好翻译整个格式字符串:

message = _("The account {account_number} doesn't exist").format(account_number)

以便完整的消息在西班牙语翻译文件中更有意义:

#: main.py:523
msgid "The account {account_number} doesn't exist"
msgstr "La cuenta {account_number} no existe."

这在翻译成自然语言时特别有用,这些自然语言的语法要求会改变句子的顺序,例如 德语 语言。