标签归档:f-string

为什么带有f字符串的f'{{{74}}}’与f'{{74}}’相同?

问题:为什么带有f字符串的f'{{{74}}}’与f'{{74}}’相同?

f字符串可从Python 3.6中获得,对于格式化字符串非常有用:

>>> n='you'
>>> f'hello {n}, how are you?'
'hello you, how are you?'

Python 3的f字符串中阅读有关它们的更多信息:改进的字符串格式语法(指南)。我发现了一个有趣的模式:

请注意,使用三重花括号将导致字符串中只有一个大括号:

>>> f"{{{74}}}"
'{74}'

但是,如果使用的括号多于三个,则可以显示更多的括号:

>>> f"{{{{74}}}}"
'{{74}}'

确实是这样:

>>> f'{74}'
'74'

>>> f'{{74}}'
'{74}'

现在,如果我们从两个传递{到三个,结果将是相同的:

>>> f'{{{74}}}'
'{74}'           # same as f'{{74}}' !

因此,我们最多需要4个!({{{{)获得两个大括号作为输出:

>>> f'{{{{74}}}}'
'{{74}}'

为什么是这样?从那时起,用两个花括号使Python需要一个额外的花括号会发生什么?

f-Strings are available from Python 3.6 and are very useful for formatting strings:

>>> n='you'
>>> f'hello {n}, how are you?'
'hello you, how are you?'

Reading more about them in Python 3’s f-Strings: An Improved String Formatting Syntax (Guide). I found an interesting pattern:

Note that using triple braces will result in there being only single braces in your string:

>>> f"{{{74}}}"
'{74}'

However, you can get more braces to show if you use more than triple braces:

>>> f"{{{{74}}}}"
'{{74}}'

And this is exactly the case:

>>> f'{74}'
'74'

>>> f'{{74}}'
'{74}'

Now if we pass from two { to three, the result is the same:

>>> f'{{{74}}}'
'{74}'           # same as f'{{74}}' !

So we need up to 4! ({{{{) to get two braces as an output:

>>> f'{{{{74}}}}'
'{{74}}'

Why is this? What happens with two braces to have Python require an extra one from that moment on?


回答 0

双括号逃脱牙套,所以没有发生插值:{{{}}}。并74保持不变的字符串,'74'

与三括号相同,外部的双括号被转义。另一方面,内部括号导致值的常规字符串插值74

也就是说,该字符串f'{{{74}}}'等效于f'{{ {74} }}',但没有空格(或等效于'{' + f'{74}' + '}')。

在用变量替换数字常量时,您可以看到区别:

In [1]: x = 74

In [2]: f'{{x}}'
Out[2]: '{x}'

In [3]: f'{{{x}}}'
Out[3]: '{74}'

Double braces escape the braces, so that no interpolation happens: {{{, and }}}. And 74 remains an unchanged string, '74'.

With triple braces, the outer double braces are escaped, same as above. The inner braces, on the other hand, lead to regular string interpolation of the value 74.

That is, the string f'{{{74}}}' is equivalent to f'{{ {74} }}', but without spaces (or, equivalently, to '{' + f'{74}' + '}').

You can see the difference when replacing the numeric constant by a variable:

In [1]: x = 74

In [2]: f'{{x}}'
Out[2]: '{x}'

In [3]: f'{{{x}}}'
Out[3]: '{74}'

如何在f字符串中使用换行符’\ n’格式化Python 3.6中的输出?

问题:如何在f字符串中使用换行符’\ n’格式化Python 3.6中的输出?

我想知道如何用f字符串以Pythonic方式格式化这种情况:

names = ['Adam', 'Bob', 'Cyril']
text = f"Winners are:\n{'\n'.join(names)}"
print(text)

问题是'\'无法在{...}f字符串的表达式部分内使用。预期Yield:

Winners are:
Adam
Bob
Cyril

I would like to know how to format this case in a Pythonic way with f-strings:

names = ['Adam', 'Bob', 'Cyril']
text = f"Winners are:\n{'\n'.join(names)}"
print(text)

The problem is that '\' cannot be used inside the {...} expression portions of an f-string. Expected output:

Winners are:
Adam
Bob
Cyril

回答 0

你不能 反斜杠不能出现在花括号内{};这样做会导致SyntaxError

>>> f'{\}'
SyntaxError: f-string expression part cannot include a backslash

这是在PEP中为f字符串指定的:

反斜杠可能不会出现在f字符串的表达式部分内,[…]

一种选择是先'\n'命名,然后再.joinf-string 内进行命名;也就是说,不使用文字:

names = ['Adam', 'Bob', 'Cyril']
nl = '\n'
text = f"Winners are:{nl}{nl.join(names)}"
print(text)

结果是:

Winners are:
Adam
Bob
Cyril

@wim指定的另一个选项是chr(10)用来获取\n返回值,然后在该处加入。f"Winners are:\n{chr(10).join(names)}"

当然,还有另一种方法是'\n'.join预先添加相应的名称:

n = "\n".join(names)
text = f"Winners are:\n{n}"

结果相同。

注意:

这是f-string和之间的细微差别之一str.format。在后者中,您可以始终使用标点符号,只要打开包含这些键的相应古怪字典即可:

>>> "{\\} {*}".format(**{"\\": 'Hello', "*": 'World!'})
"Hello World!"

(请不要这样做。)

在前一种情况下,标点符号是不允许的,因为您不能使用它们的标识符。


撇开:我肯定会选择printformat,因为其他答案也可以替代。我给出的选项仅在由于某些原因必须使用f字符串的情况下适用。

仅仅因为有些新事物,并不意味着您应该尝试用它做一切;-)

You can’t. Backslashes cannot appear inside the curly braces {}; doing so results in a SyntaxError:

>>> f'{\}'
SyntaxError: f-string expression part cannot include a backslash

This is specified in the PEP for f-strings:

Backslashes may not appear inside the expression portions of f-strings, […]

One option is assinging '\n' to a name and then .join on that inside the f-string; that is, without using a literal:

names = ['Adam', 'Bob', 'Cyril']
nl = '\n'
text = f"Winners are:{nl}{nl.join(names)}"
print(text)

Results in:

Winners are:
Adam
Bob
Cyril

Another option, as specified by @wim, is to use chr(10) to get \n returned and then join there. f"Winners are:\n{chr(10).join(names)}"

Yet another, of course, is to '\n'.join beforehand and then add the name accordingly:

n = "\n".join(names)
text = f"Winners are:\n{n}"

which results in the same output.

Note:

This is one of the small differences between f-strings and str.format. In the latter, you can always use punctuation granted that a corresponding wacky dict is unpacked that contains those keys:

>>> "{\\} {*}".format(**{"\\": 'Hello', "*": 'World!'})
"Hello World!"

(Please don’t do this.)

In the former, punctuation isn’t allowed because you can’t have identifiers that use them.


Aside: I would definitely opt for print or format, as the other answers suggest as an alternative. The options I’ve given only apply if you must for some reason use f-strings.

Just because something is new, doesn’t mean you should try and do everything with it ;-)


回答 1

您不需要f字符串或其他格式化程序即可使用分隔符打印字符串列表。只需使用sep关键字参数即可print()

names = ['Adam', 'Bob', 'Cyril']
print('Winners are:', *names, sep='\n')

输出:

Winners are:
Adam
Bob
Cyril

就是说,使用str.join()/str.format()这里比任何f字符串解决方法更容易理解。

print('\n'.join(['Winners are:', *names]))
print('Winners are:\n{}'.format('\n'.join(names)))

You don’t need f-strings or other formatters to print a list of strings with a separator. Just use the sep keyword argument to print():

names = ['Adam', 'Bob', 'Cyril']
print('Winners are:', *names, sep='\n')

Output:

Winners are:
Adam
Bob
Cyril

That said, using str.join()/str.format() here would arguably be simpler and more readable than any f-string workaround:

print('\n'.join(['Winners are:', *names]))
print('Winners are:\n{}'.format('\n'.join(names)))

回答 2

您不能像其他人所说的那样在f字符串中使用反斜杠,但是您可以使用来解决这个问题os.linesep(尽管请注意,并非\n在所有平台上都使用反斜杠,除非读/写二进制文件,否则不建议这样做;请参阅Rick的评论):

>>> import os
>>> names = ['Adam', 'Bob', 'Cyril']
>>> print(f"Winners are:\n{os.linesep.join(names)}")
Winners are:
Adam
Bob
Cyril 

或以一种不太易读的方式,但保证是\n,方法是chr()

>>> print(f"Winners are:\n{chr(10).join(names)}")
Winners are:
Adam
Bob
Cyril

You can’t use backslashes in f-strings as others have said, but you could step around this using os.linesep (although note this won’t be \n on all platforms, and is not recommended unless reading/writing binary files; see Rick’s comments):

>>> import os
>>> names = ['Adam', 'Bob', 'Cyril']
>>> print(f"Winners are:\n{os.linesep.join(names)}")
Winners are:
Adam
Bob
Cyril 

Or perhaps in a less readable way, but guaranteed to be \n, with chr():

>>> print(f"Winners are:\n{chr(10).join(names)}")
Winners are:
Adam
Bob
Cyril

回答 3

其他答案给出了有关如何将换行符放入f字符串字段的想法。但是,我认为对于OP给出的示例(可能指示OP的实际用例),实际上不应该使用这些想法。

使用f字符串的全部目的是提高代码的可读性。f字符串是您无法使用的format。请仔细考虑是否对此有更多可读性(如果可以的话):

f"Winners are:\n{'\n'.join(names)}"

…或这个:

newline = '\n'
f"Winners are:\n{newline.join(names)}"

…或这个:

"Winners are:\n{chr(10).join(names)}"

与这个:

"Winners are:\n{}".format('\n'.join(names))

最后一种方法至少具有可读性,如果不是更多的话。

简而言之:当您需要螺丝起子时,不要只用锤子,因为那是新的有光泽的工具。读取代码的频率远高于写入代码。

对于其他用例,是的,这个chr(10)想法或newline想法可能是适当的。但不是给定的。

The other answers give ideas for how to put the newline character into a f-string field. However, I would argue that for the example the OP gave (which may or may not be indicative of OP’s actual use case), none of these ideas should actually be used.

The entire point of using f-strings is increasing code readability. There is nothing you can do with f-strings that you cannot do with format. Consider carefully whether there is anything more readable about this (if you could do it):

f"Winners are:\n{'\n'.join(names)}"

…or this:

newline = '\n'
f"Winners are:\n{newline.join(names)}"

…or this:

"Winners are:\n{chr(10).join(names)}"

vs. this:

"Winners are:\n{}".format('\n'.join(names))

The last way is at least as readable, if not more so.

In short: don’t use a hammer when you need a screwdriver just because you have a shiny new one. Code is read much more often than it is written.

For other use cases, yes, it’s possible the chr(10) idea or newline idea may be appropriate. But not for the one given.


如何推迟/推迟对f弦的评估?

问题:如何推迟/推迟对f弦的评估?

我正在使用模板字符串生成一些文件,为此我喜欢新的f字符串的简洁性,以减少类似以下内容的我以前的模板代码:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a.format(**locals()))

现在,我可以直接替换变量:

names = ["foo", "bar"]
for name in names:
    print (f"The current name is {name}")

但是,有时在其他地方定义模板是有意义的-在代码中较高的位置,或者从文件或其他内容中导入模板。这意味着模板是带有格式标记的静态字符串。字符串上必须发生一些事情,以告诉解释器将字符串解释为新的f字符串,但是我不知道是否有这种事情。

有什么方法可以引入字符串并将其解释为f字符串,从而避免使用.format(**locals())调用?

理想情况下,我希望能够像这样进行编码…(magic_fstring_function我不理解的部分在哪里出现):

template_a = f"The current name is {name}"
# OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read())
names = ["foo", "bar"]
for name in names:
    print (template_a)

…具有所需的输出(无需两次读取文件):

The current name is foo
The current name is bar

…但是我得到的实际输出是:

The current name is {name}
The current name is {name}

I am using template strings to generate some files and I love the conciseness of the new f-strings for this purpose, for reducing my previous template code from something like this:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a.format(**locals()))

Now I can do this, directly replacing variables:

names = ["foo", "bar"]
for name in names:
    print (f"The current name is {name}")

However, sometimes it makes sense to have the template defined elsewhere — higher up in the code, or imported from a file or something. This means the template is a static string with formatting tags in it. Something would have to happen to the string to tell the interpreter to interpret the string as a new f-string, but I don’t know if there is such a thing.

Is there any way to bring in a string and have it interpreted as an f-string to avoid using the .format(**locals()) call?

Ideally I want to be able to code like this… (where magic_fstring_function is where the part I don’t understand comes in):

template_a = f"The current name is {name}"
# OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read())
names = ["foo", "bar"]
for name in names:
    print (template_a)

…with this desired output (without reading the file twice):

The current name is foo
The current name is bar

…but the actual output I get is:

The current name is {name}
The current name is {name}

回答 0

这是完整的“理想2”。

它不是f字符串,它甚至不使用f字符串,但可以按要求进行操作。语法完全符合规定。没有使用安全性,因为我们没有使用eval()

它使用了一个小类并实现了__str__由print自动调用的类。为了逃避该类的有限范围,我们使用该inspect模块向上跳一帧并查看调用者可以访问的变量。

import inspect

class magic_fstring_function:
    def __init__(self, payload):
        self.payload = payload
    def __str__(self):
        vars = inspect.currentframe().f_back.f_globals.copy()
        vars.update(inspect.currentframe().f_back.f_locals)
        return self.payload.format(**vars)

template = "The current name is {name}"

template_a = magic_fstring_function(template)

# use it inside a function to demonstrate it gets the scoping right
def new_scope():
    names = ["foo", "bar"]
    for name in names:
        print(template_a)

new_scope()
# The current name is foo
# The current name is bar

Here’s a complete “Ideal 2”.

It’s not an f-string—it doesn’t even use f-strings—but it does as requested. Syntax exactly as specified. No security headaches since we are not using eval().

It uses a little class and implements __str__ which is automatically called by print. To escape the limited scope of the class we use the inspect module to hop one frame up and see the variables the caller has access to.

import inspect

class magic_fstring_function:
    def __init__(self, payload):
        self.payload = payload
    def __str__(self):
        vars = inspect.currentframe().f_back.f_globals.copy()
        vars.update(inspect.currentframe().f_back.f_locals)
        return self.payload.format(**vars)

template = "The current name is {name}"

template_a = magic_fstring_function(template)

# use it inside a function to demonstrate it gets the scoping right
def new_scope():
    names = ["foo", "bar"]
    for name in names:
        print(template_a)

new_scope()
# The current name is foo
# The current name is bar

回答 1

这意味着模板是带有格式标记的静态字符串

是的,这就是为什么我们要使用带有替换字段和的文字.format,因此我们可以随时调用format它来替换这些字段。

字符串上必须发生一些事情,以告知解释器将字符串解释为新的f字符串

那是前缀f/F。您可以将其包装在一个函数中,并在调用期间推迟评估,但是当然会产生额外的开销:

template_a = lambda: f"The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a())

打印出:

The current name is foo
The current name is bar

但感觉不对,并受到以下事实的限制:您只能窥视替换中的全局命名空间。在需要本地名称的情况下尝试使用它会惨遭失败,除非将其作为参数传递给字符串(这完全是关键)。

有什么方法可以引入字符串并将其解释为f字符串,从而避免使用.format(**locals())调用?

除了功能(包括限制)外,不行,所以不妨坚持.format

This means the template is a static string with formatting tags in it

Yes, that’s exactly why we have literals with replacement fields and .format, so we can replace the fields whenever we like by calling format on it.

Something would have to happen to the string to tell the interpreter to interpret the string as a new f-string

That’s the prefix f/F. You could wrap it in a function and postpone the evaluation during call time but of course that incurs extra overhead:

template_a = lambda: f"The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a())

Which prints out:

The current name is foo
The current name is bar

but feels wrong and is limited by the fact that you can only peek at the global namespace in your replacements. Trying to use it in a situation which requires local names will fail miserably unless passed to the string as arguments (which totally beats the point).

Is there any way to bring in a string and have it interpreted as an f-string to avoid using the .format(**locals()) call?

Other than a function (limitations included), nope, so might as well stick with .format.


回答 2

一种将字符串评估为f字符串(具有完整功能)的简洁方法是使用以下函数:

def fstr(template):
    return eval(f"f'{template}'")

然后,您可以执行以下操作:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print(fstr(template_a))
# The current name is foo
# The current name is bar

并且,与许多其他建议的解决方案相比,您还可以执行以下操作:

template_b = "The current name is {name.upper() * 2}"
for name in names:
    print(fstr(template_b))
# The current name is FOOFOO
# The current name is BARBAR

A concise way to have a string evaluated as an f-string (with its full capabilities) is using following function:

def fstr(template):
    return eval(f"f'{template}'")

Then you can do:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print(fstr(template_a))
# The current name is foo
# The current name is bar

And, in contrast to many other proposed solutions, you can also do:

template_b = "The current name is {name.upper() * 2}"
for name in names:
    print(fstr(template_b))
# The current name is FOOFOO
# The current name is BARBAR

回答 3

一架F-string是简单的创建一个格式化字符串,更换了更为简洁的方式.format(**names)f。如果您不希望以这种方式立即评估字符串,请不要将其设置为f字符串。将其另存为普通字符串文字,然后format在以后要进行插值时再调用它,就像以前一样。

当然,可以使用替代eval

template.txt

f’当前名称为{name}’

码:

>>> template_a = open('template.txt').read()
>>> names = 'foo', 'bar'
>>> for name in names:
...     print(eval(template_a))
...
The current name is foo
The current name is bar

但后来你已经设法做的是替换str.formateval,这肯定是不值得的。只需继续使用常规字符串即可format

An f-string is simply a more concise way of creating a formatted string, replacing .format(**names) with f. If you don’t want a string to be immediately evaluated in such a manner, don’t make it an f-string. Save it as an ordinary string literal, and then call format on it later when you want to perform the interpolation, as you have been doing.

Of course, there is an alternative with eval.

template.txt:

f’The current name is {name}’

Code:

>>> template_a = open('template.txt').read()
>>> names = 'foo', 'bar'
>>> for name in names:
...     print(eval(template_a))
...
The current name is foo
The current name is bar

But then all you’ve managed to do is replace str.format with eval, which is surely not worth it. Just keep using regular strings with a format call.


回答 4

使用.format并不是此问题的正确答案。Python f字符串与str.format()模板非常不同…它们可以包含代码或其他昂贵的操作-因此需要推迟。

这是一个延迟记录器的示例。这使用了logging.getLogger的常规序言,但是随后添加了仅在日志级别正确的情况下解释f字符串的新函数。

log = logging.getLogger(__name__)

def __deferred_flog(log, fstr, level, *args):
    if log.isEnabledFor(level):
        import inspect
        frame = inspect.currentframe().f_back.f_back
        try:
            fstr = 'f"' + fstr + '"'
            log.log(level, eval(fstr, frame.f_globals, frame.f_locals))
        finally:
            del frame
log.fdebug = lambda fstr, *args: __deferred_flog(log, fstr, logging.DEBUG, *args)
log.finfo = lambda fstr, *args: __deferred_flog(log, fstr, logging.INFO, *args)

这样做的优点是能够执行以下操作: log.fdebug("{obj.dump()}")….除非启用调试,否则不转储对象。

恕我直言:这应该是f字符串的默认操作,但是现在为时已晚。F字符串评估可能会产生大量和意想不到的副作用,以延迟的方式发生会改变程序的执行。

为了适当延迟f字符串,python需要某种方式来显式切换行为。也许使用字母“ g”?;)

Using .format is not a correct answer to this question. Python f-strings are very different from str.format() templates … they can contain code or other expensive operations – hence the need for deferral.

Here’s an example of a deferred logger. This uses the normal preamble of logging.getLogger, but then adds new functions that interpret the f-string only if the log level is correct.

log = logging.getLogger(__name__)

def __deferred_flog(log, fstr, level, *args):
    if log.isEnabledFor(level):
        import inspect
        frame = inspect.currentframe().f_back.f_back
        try:
            fstr = 'f"' + fstr + '"'
            log.log(level, eval(fstr, frame.f_globals, frame.f_locals))
        finally:
            del frame
log.fdebug = lambda fstr, *args: __deferred_flog(log, fstr, logging.DEBUG, *args)
log.finfo = lambda fstr, *args: __deferred_flog(log, fstr, logging.INFO, *args)

This has the advantage of being able to do things like: log.fdebug("{obj.dump()}") …. without dumping the object unless debugging is enabled.

IMHO: This should have been the default operation of f-strings, however now it’s too late. F-string evaluation can have massive and unintended side-effects, and having that happen in a deferred manner will change program execution.

In order to make f-strings properly deferred, python would need some way of explicitly switching behavior. Maybe use the letter ‘g’? ;)

It has been pointed out that deferred logging shouldn’t crash if there’s a bug in the string converter. The above solution can do this as well, change the finally: to except:, and stick a log.exception in there.


回答 5

您想要的东西似乎被认为是Python增强功能

同时-从链接的讨论中-以下似乎是一个不需要使用的合理解决方法eval()

class FL:
    def __init__(self, func):
        self.func = func
    def __str__(self):
        return self.func()


template_a = FL(lambda: f"The current name, number is {name!r}, {number+1}")
names = "foo", "bar"
numbers = 40, 41
for name, number in zip(names, numbers):
    print(template_a)

输出:

The current name, number is 'foo', 41
The current name, number is 'bar', 42

What you want appears to be being considered as a Python enhancement.

Meanwhile — from the linked discussion — the following seems like it would be a reasonable workaround that doesn’t require using eval():

class FL:
    def __init__(self, func):
        self.func = func
    def __str__(self):
        return self.func()


template_a = FL(lambda: f"The current name, number is {name!r}, {number+1}")
names = "foo", "bar"
numbers = 40, 41
for name, number in zip(names, numbers):
    print(template_a)

Output:

The current name, number is 'foo', 41
The current name, number is 'bar', 42

回答 6

kadee答案的启发,以下内容可用于定义deferred-f-string类。

class FStr:
    def __init__(self, s):
        self._s = s
    def __repr__(self):
        return eval(f"f'{self._s}'")

...

template_a = FStr('The current name is {name}')

names = ["foo", "bar"]
for name in names:
    print (template_a)

这正是这个问题要问的

inspired by the answer by kadee, the following can be used to define a deferred-f-string class.

class FStr:
    def __init__(self, s):
        self._s = s
    def __repr__(self):
        return eval(f"f'{self._s}'")

...

template_a = FStr('The current name is {name}')

names = ["foo", "bar"]
for name in names:
    print (template_a)

which is exactly what the question asked for


回答 7

或者也许不使用f字符串,只需格式化:

fun = "The curent name is {name}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name=name))

在没有名称的版本中:

fun = "The curent name is {}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name))

Or maybe do not use f-strings, just format:

fun = "The curent name is {name}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name=name))

In version without names:

fun = "The curent name is {}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name))

回答 8

怎么样:

s = 'Hi, {foo}!'

s
> 'Hi, {foo}!'

s.format(foo='Bar')
> 'Hi, Bar!'

How about:

s = 'Hi, {foo}!'

s
> 'Hi, {foo}!'

s.format(foo='Bar')
> 'Hi, Bar!'

回答 9

使用f字符串的建议。在发生模板的逻辑级别上进行评估,并将其作为生成器传递。您可以使用f弦在任意位置解开它

In [46]: names = (i for i in ('The CIO, Reed', 'The homeless guy, Arnot', 'The security guard Spencer'))

In [47]: po = (f'Strangely, {next(names)} has a nice {i}' for i in (" nice house", " fast car", " big boat"))

In [48]: while True:  
...:     try:  
...:         print(next(po))  
...:     except StopIteration:  
...:         break  
...:       
Strangely, The CIO, Reed has a nice  nice house  
Strangely, The homeless guy, Arnot has a nice  fast car  
Strangely, The security guard Spencer has a nice  big boat  

A suggestion that uses f-strings. Do your evaluation on the logical level where the templating is occurring and pass it as a generator. You can unwind it at whatever point you choose, using f-strings

In [46]: names = (i for i in ('The CIO, Reed', 'The homeless guy, Arnot', 'The security guard Spencer'))

In [47]: po = (f'Strangely, {next(names)} has a nice {i}' for i in (" nice house", " fast car", " big boat"))

In [48]: while True:  
...:     try:  
...:         print(next(po))  
...:     except StopIteration:  
...:         break  
...:       
Strangely, The CIO, Reed has a nice  nice house  
Strangely, The homeless guy, Arnot has a nice  fast car  
Strangely, The security guard Spencer has a nice  big boat  

字符串格式:%与.format

问题:字符串格式:%与.format

Python 2.6引入的str.format()方法与现有%运算符的语法略有不同。哪个更好,什么情况下适合?

  1. 以下使用每种方法并具有相同的结果,那么有什么区别?

    #!/usr/bin/python
    sub1 = "python string!"
    sub2 = "an arg"
    
    a = "i am a %s" % sub1
    b = "i am a {0}".format(sub1)
    
    c = "with %(kwarg)s!" % {'kwarg':sub2}
    d = "with {kwarg}!".format(kwarg=sub2)
    
    print a    # "i am a python string!"
    print b    # "i am a python string!"
    print c    # "with an arg!"
    print d    # "with an arg!"
    
  2. 此外,何时在Python中进行字符串格式化?例如,如果我的日志记录级别设置为HIGH,那么执行以下%操作是否还会对我有所帮助?如果是这样,有办法避免这种情况吗?

    log.debug("some debug info: %s" % some_info)

Python 2.6 introduced the str.format() method with a slightly different syntax from the existing % operator. Which is better and for what situations?

  1. The following uses each method and has the same outcome, so what is the difference?

    #!/usr/bin/python
    sub1 = "python string!"
    sub2 = "an arg"
    
    a = "i am a %s" % sub1
    b = "i am a {0}".format(sub1)
    
    c = "with %(kwarg)s!" % {'kwarg':sub2}
    d = "with {kwarg}!".format(kwarg=sub2)
    
    print a    # "i am a python string!"
    print b    # "i am a python string!"
    print c    # "with an arg!"
    print d    # "with an arg!"
    
  2. Furthermore when does string formatting occur in Python? For example, if my logging level is set to HIGH will I still take a hit for performing the following % operation? And if so, is there a way to avoid this?

    log.debug("some debug info: %s" % some_info)
    

回答 0

要回答您的第一个问题… .format在许多方面似乎都更加复杂。令人烦恼的%是它如何可以采用变量或元组。您会认为以下各项将始终有效:

"hi there %s" % name

但是,如果name碰巧(1, 2, 3),它将抛出一个TypeError。为了确保它始终打印,您需要执行

"hi there %s" % (name,)   # supply the single argument as a single-item tuple

真丑。.format没有那些问题。同样在您给出的第二个示例中,该.format示例看起来更加简洁。

为什么不使用它?

  • 不知道(我在阅读本文之前)
  • 必须与Python 2.5兼容

为了回答您的第二个问题,字符串格式化与其他任何操作都同时发生-计算字符串格式化表达式时。而且,Python并不是一种惰性语言,它会在调用函数之前先对表达式求值,因此在您的log.debug示例中,表达式"some debug info: %s"%some_info将首先求值,例如"some debug info: roflcopters are active",然后将该字符串传递给log.debug()

To answer your first question… .format just seems more sophisticated in many ways. An annoying thing about % is also how it can either take a variable or a tuple. You’d think the following would always work:

"hi there %s" % name

yet, if name happens to be (1, 2, 3), it will throw a TypeError. To guarantee that it always prints, you’d need to do

"hi there %s" % (name,)   # supply the single argument as a single-item tuple

which is just ugly. .format doesn’t have those issues. Also in the second example you gave, the .format example is much cleaner looking.

Why would you not use it?

  • not knowing about it (me before reading this)
  • having to be compatible with Python 2.5

To answer your second question, string formatting happens at the same time as any other operation – when the string formatting expression is evaluated. And Python, not being a lazy language, evaluates expressions before calling functions, so in your log.debug example, the expression "some debug info: %s"%some_infowill first evaluate to, e.g. "some debug info: roflcopters are active", then that string will be passed to log.debug().


回答 1

afaik,模运算符(%)无法做到的事情:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

结果

12 22222 45 22222 103 22222 6 22222

很有用。

另一点:format()作为函数,可以用作其他函数的参数:

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

结果是:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00

Something that the modulo operator ( % ) can’t do, afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

result

12 22222 45 22222 103 22222 6 22222

Very useful.

Another point: format(), being a function, can be used as an argument in other functions:

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

Results in:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00

回答 2

假设您正在使用Python的logging模块,则可以将字符串格式参数作为参数传递给.debug()方法,而不必自己进行格式设置:

log.debug("some debug info: %s", some_info)

除非记录器实际记录某些内容,否则可以避免进行格式化。

Assuming you’re using Python’s logging module, you can pass the string formatting arguments as arguments to the .debug() method rather than doing the formatting yourself:

log.debug("some debug info: %s", some_info)

which avoids doing the formatting unless the logger actually logs something.


回答 3

从Python 3.6(2016)开始,您可以使用f字符串替换变量:

>>> origin = "London"
>>> destination = "Paris"
>>> f"from {origin} to {destination}"
'from London to Paris'

注意f"前缀。如果您在Python 3.5或更早版本中尝试此操作,则会看到一个SyntaxError

参见https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

As of Python 3.6 (2016) you can use f-strings to substitute variables:

>>> origin = "London"
>>> destination = "Paris"
>>> f"from {origin} to {destination}"
'from London to Paris'

Note the f" prefix. If you try this in Python 3.5 or earlier, you’ll get a SyntaxError.

See https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings


回答 4

PEP 3101提议%用Python 3中新的高级字符串格式替换运算符,这将是默认格式。

PEP 3101 proposes the replacement of the % operator with the new, advanced string formatting in Python 3, where it would be the default.


回答 5

但是请小心,刚才我在尝试%.format现有代码替换所有内容时发现了一个问题:'{}'.format(unicode_string)将尝试对unicode_string进行编码,并且可能会失败。

只需查看以下Python交互式会话日志即可:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s只是一个字符串(在Python3中称为“字节数组”),并且u是Unicode字符串(在Python3中称为“字符串”):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

当将Unicode对象作为%运算符的参数时,即使原始字符串不是Unicode,它也会产生一个Unicode字符串:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

但该.format函数将引发“ UnicodeEncodeError”:

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

并且仅当原始字符串为Unicode时,它才可以与Unicode参数一起使用。

; '{}'.format(u'i')
'i'

或者参数字符串可以转换为字符串(所谓的“字节数组”)

But please be careful, just now I’ve discovered one issue when trying to replace all % with .format in existing code: '{}'.format(unicode_string) will try to encode unicode_string and will probably fail.

Just look at this Python interactive session log:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s is just a string (called ‘byte array’ in Python3) and u is a Unicode string (called ‘string’ in Python3):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

When you give a Unicode object as a parameter to % operator it will produce a Unicode string even if the original string wasn’t Unicode:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

but the .format function will raise “UnicodeEncodeError”:

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

and it will work with a Unicode argument fine only if the original string was Unicode.

; '{}'.format(u'i')
'i'

or if argument string can be converted to a string (so called ‘byte array’)


回答 6

的另一个优点.format(我没有在答案中看到):它可以具有对象属性。

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

或者,作为关键字参数:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

%据我所知,这是不可能的。

Yet another advantage of .format (which I don’t see in the answers): it can take object properties.

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

Or, as a keyword argument:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

This is not possible with % as far as I can tell.


回答 7

%format我的测试提供更好的性能。

测试代码:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

结果:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

结果

> format: 0.5864730989560485
> %: 0.013593495357781649

它在Python2中看起来很小,而在Python3中%则比快得多format

感谢@Chris Cogdon提供示例代码。

编辑1:

2019年7月在Python 3.7.2中再次测试。

结果:

> format: 0.86600608
> %: 0.630180146

没有太大的区别。我想Python正在逐步完善。

编辑2:

在有人在评论中提到python 3的f字符串后,我在python 3.7.2下对以下代码进行了测试:

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))
print('f-string:', timeit.timeit("f'{1}{1.23}{\"hello\"}'"))

结果:

format: 0.8331376779999999
%: 0.6314778750000001
f-string: 0.766649943

看来f-string仍然比慢,%但比慢format

% gives better performance than format from my test.

Test code:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

Result:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

Result

> format: 0.5864730989560485
> %: 0.013593495357781649

It looks in Python2, the difference is small whereas in Python3, % is much faster than format.

Thanks @Chris Cogdon for the sample code.

Edit 1:

Tested again in Python 3.7.2 in July 2019.

Result:

> format: 0.86600608
> %: 0.630180146

There is not much difference. I guess Python is improving gradually.

Edit 2:

After someone mentioned python 3’s f-string in comment, I did a test for the following code under python 3.7.2 :

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))
print('f-string:', timeit.timeit("f'{1}{1.23}{\"hello\"}'"))

Result:

format: 0.8331376779999999
%: 0.6314778750000001
f-string: 0.766649943

It seems f-string is still slower than % but better than format.


回答 8

正如我今天发现的那样,通过格式化字符串的旧方法%不支持Decimalpython的用于十进制定点和浮点算术的模块。

示例(使用Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

输出:

0.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312312239239000000000000000000

当然可能有解决方法,但是您仍然可以考虑立即使用该format()方法。

As I discovered today, the old way of formatting strings via % doesn’t support Decimal, Python’s module for decimal fixed point and floating point arithmetic, out of the box.

Example (using Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

Output:

0.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

There surely might be work-arounds but you still might consider using the format() method right away.


回答 9

如果您的python> = 3.6,则F字符串格式的文字是您的新朋友。

它更简单,更干净,性能更好。

In [1]: params=['Hello', 'adam', 42]

In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

If your python >= 3.6, F-string formatted literal is your new friend.

It’s more simple, clean, and better performance.

In [1]: params=['Hello', 'adam', 42]

In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

回答 10

附带说明,您不必为了提高性能而在日志记录中使用新样式格式。您可以将任何实现了magic方法的对象传递给logging.debuglogging.info等等__str__。当日志记录模块决定必须发出您的消息对象(无论它是什么)时,它将str(message_object)在发出消息之前先进行调用。因此,您可以执行以下操作:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

所有这些都在Python 3文档(https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles)中进行了描述。但是,它也可以在Python 2.6中使用(https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages)。

使用该技术的优点之一是它允许使用惰性值,例如expensive_func上面的函数,这是事实,除了格式风格不可知。这为Python文档中的建议提供了更优雅的替代方法:https : //docs.python.org/2.6/library/logging.html#optimization

As a side note, you don’t have to take a performance hit to use new style formatting with logging. You can pass any object to logging.debug, logging.info, etc. that implements the __str__ magic method. When the logging module has decided that it must emit your message object (whatever it is), it calls str(message_object) before doing so. So you could do something like this:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

This is all described in the Python 3 documentation (https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles). However, it will work with Python 2.6 as well (https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages).

One of the advantages of using this technique, other than the fact that it’s formatting-style agnostic, is that it allows for lazy values e.g. the function expensive_func above. This provides a more elegant alternative to the advice being given in the Python docs here: https://docs.python.org/2.6/library/logging.html#optimization.


回答 11

一种%可能有用的情况是格式化正则表达式时。例如,

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

加薪IndexError。在这种情况下,您可以使用:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

这样可以避免将正则表达式写为'{type_names} [a-z]{{2}}'。当您有两个正则表达式时,这很有用,其中一个正则表达式单独使用而没有格式,但是两个正则表达式的连接都已格式化。

One situation where % may help is when you are formatting regex expressions. For example,

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

raises IndexError. In this situation, you can use:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

This avoids writing the regex as '{type_names} [a-z]{{2}}'. This can be useful when you have two regexes, where one is used alone without format, but the concatenation of both is formatted.


回答 12

我要补充一点,从3.6版开始,我们可以像下面这样使用fstrings

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

哪个给

我叫约翰·史密斯

一切都转换为字符串

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

结果:

mylist = [‘foo’,’bar’]

您可以像其他格式一样传递函数

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

举个例子

您好,这是日期:16/04/2018

I would add that since version 3.6, we can use fstrings like the following

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

Which give

My name is john smith

Everything is converted to strings

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

Result:

mylist = [‘foo’, ‘bar’]

you can pass function, like in others formats method

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

Giving for example

Hello, here is the date : 16/04/2018


回答 13

对于python版本> = 3.6(请参阅PEP 498

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'

For python version >= 3.6 (see PEP 498)

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'

回答 14

Python 3.6.7比较:

#!/usr/bin/env python
import timeit

def time_it(fn):
    """
    Measure time of execution of a function
    """
    def wrapper(*args, **kwargs):
        t0 = timeit.default_timer()
        fn(*args, **kwargs)
        t1 = timeit.default_timer()
        print("{0:.10f} seconds".format(t1 - t0))
    return wrapper


@time_it
def new_new_format(s):
    print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")


@time_it
def new_format(s):
    print("new_format:", "{0} {1} {2} {3} {4}".format(*s))


@time_it
def old_format(s):
    print("old_format:", "%s %s %s %s %s" % s)


def main():
    samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),) 
    for s in samples:
        new_new_format(s)
        new_format(s)
        old_format(s)
        print("-----")


if __name__ == '__main__':
    main()

输出:

new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----

Python 3.6.7 comparative:

#!/usr/bin/env python
import timeit

def time_it(fn):
    """
    Measure time of execution of a function
    """
    def wrapper(*args, **kwargs):
        t0 = timeit.default_timer()
        fn(*args, **kwargs)
        t1 = timeit.default_timer()
        print("{0:.10f} seconds".format(t1 - t0))
    return wrapper


@time_it
def new_new_format(s):
    print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")


@time_it
def new_format(s):
    print("new_format:", "{0} {1} {2} {3} {4}".format(*s))


@time_it
def old_format(s):
    print("old_format:", "%s %s %s %s %s" % s)


def main():
    samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),) 
    for s in samples:
        new_new_format(s)
        new_format(s)
        old_format(s)
        print("-----")


if __name__ == '__main__':
    main()

Output:

new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----

回答 15

但是有一件事是,如果您嵌套了花括号,则不能使用格式但%可以使用。

例:

>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>> 

But one thing is that also if you have nested curly-braces, won’t work for format but % will work.

Example:

>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>>