Python中的链式字符串格式化

4 投票
4 回答
4012 浏览
提问于 2025-04-16 01:25

在处理一些SQL格式化的时候,我惊讶地发现我可以把字符串格式化器串联起来使用:

def get_sql(table, limit=True):
    sql = "select report_date from %s"
    if limit:
        result = "%s limit 1" % sql % table
    else:
        result = sql % table
    return result

这样做可以吗?有没有什么理由不这样做?

4 个回答

2

没错,像这样把字符串格式化连起来是可以的,尽管这是我第一次看到这么用(而且用得很糟糕,别忘了这一点)!

原因是同类型的运算符是从左到右处理的(这叫“左结合性”),不过有几个例外,比如指数运算 ** 和比较运算 a<b<c

所以就像这样:

>>> 1 - 2 - 3    # equals to (1-2)-3
-4
>>> 16 / 4 / 2   # equals to (16 /4) /2,  NOT 16 / (4 / 2)
2

同样,s1 % s2 % s3 其实等于 (s1 % s2) % s3

哦,对了,不管 s1、s2 和 s3 是字符串还是数字都没关系——编译器在编译的时候并不知道这一点,只有在运行时才会确定 % 是表示“除法的余数”(如果 s1 是数字)还是字符串格式化(如果 s1 是字符串)。

3

这完全是合理的。

字符串格式化的“单个参数”形式其实是一个特殊情况——当有多个项目时,通常会使用元组,这样就能更清楚地说明为什么这样做是可以的。

result = "%s limit 1" % (sql % (table,),)

这段话最初是为了鼓励提问者,告诉他们支持多种格式是语言的一个合法特性,但正如Nas Banov所评论的,这段话读起来像是在解释它是怎么工作的(而且代码写得也不太对)。它并不是像这段话所暗示的那样从右到左构建字符串,而是必须从左到右构建(也就是说是有结合性的)。操作符必须在左边接受一个字符串并返回一个字符串,但在右边可以接受一个非字符串(比如元组)。因为你不能对一对元组使用%运算符,所以它不可能反向工作。

>>> "%s %f %s" % ( "%d", 0.1, "%d %d" ) % (1,2,3)
'1 0.100000 2 3'

不过,这可能会导致代码变得复杂或混乱,所以我个人建议尽量少用。

你可以这样处理你的例子:

def get_sql(table, limit=True):
    sql = "select report_date from"
    strlimit = ""
    if limit:
        strlimit = "limit 1"
    return "%s %s"%(sql, strlimit)
4

这样做是有道理的,因为像这样的语句:

'some value goes here %s' % value

实际上会返回一个字符串。把它看成这样可能更容易理解:

result = ("%s limit 1" % sql) % table

这样做没有什么明显的问题,但把多个操作连在一起可能会让你很难找出错误的来源。

比如说,这样做是没问题的:

>>> sql = 'a value of %s'
>>> x = 'some string %s with stuff'
>>> y = 'VALUE'
>>> x % sql % y
'some string a value of VALUE with stuff'

但是如果里面有格式错误(我知道这个例子有点极端,但能说明问题):

>>> sql = 'a value of %d'
>>> x = 'some string %d with stuff'
>>> y = 123    
>>> x % sql % y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: %d format: a number is required, not str

你根本不清楚是哪一个 %d 引起了错误。因此,我建议把每个格式化操作分开,尽量每行只用一个 % 格式化符,这样错误追踪时就能准确指向出问题的那一行和格式化符。

另外,采用每行一个格式化符的方式,也会让其他人阅读你的代码时轻松很多,更容易理解发生了什么。

撰写回答