我可以将 Python 的 3.6 格式化字符串文字(f 字符串)导入旧的 3.x、2.x Python 吗?

Can I import Python's 3.6's formatted string literals (f-strings) into older 3.x, 2.x Python?

新的 Python 3.6 f-strings 对我来说似乎是弦乐可用性的一个巨大飞跃,我很乐意加入并全心全意地在可能 运行 的新项目中采用它们在年长的口译员身上。 2.7、3.3-3.5 支持会很好,但至少我想在 Python 3.5 代码库中使用它们。我如何导入 3.6 的格式化字符串文字以供旧解释器使用?

我知道像 f"Foo is {age} {units} old" 这样的格式化字符串文字不是重大更改,因此不会包含在 from __future__ import ... 调用中。但是更改没有向后移植(AFAIK)我需要确保我用 f-strings 编写的任何新代码都只是 运行 在 Python 3.6+ 上,这对很多人来说都是一个交易破坏者项目数。

不幸的是,如果你想使用它,你必须要求 Python 3.6+,与矩阵乘法运算符 @Python 3.5+yield from 相同(Python 3.4+我认为)

这些更改了代码的解释方式,因此在旧版本中导入时会抛出语法错误。这意味着您需要将它们放在某个地方,这些地方不会在旧 Python 中导入或由 evalexec 保护(我不推荐后两者!)。

所以是的,你是对的,如果你想支持多个 python 版本,你不能轻易使用它们。

f-strings 是由解释器在标记 f 前缀时创建的 - 仅此功能将扼杀任何兼容性机会。

最接近的镜头是使用关键字格式,例如

'Foo is {age} {units} old'.format(age=age, units=units)

在兼容性要求终止时可以更轻松地重构。

future-fstrings 将 f-strings 引入 Python 2.7 脚本。 (我根据文档假设 3.3-3.5。)

通过 pip install future-fstrings 进行 pip 安装后,您必须在代码顶部放置一个特殊的行。那一行是:

# -*- coding: future_fstrings -*-

然后您可以在代码中使用格式化字符串文字 (f-strings):

# -*- coding: future_fstrings -*-
var = 'f-string'
print(f'hello world, this is an {var}')

这是我使用的:

text = "Foo is {age} {units} old".format(**locals())

它解压 (**) locals() 返回的字典,它把你所有的局部变量作为一个字典 {variable_name: value}

注意 这不适用于在外部作用域中声明的变量,除非您使用 nonlocal 将其导入本地作用域(Python 3.0+ ).

你也可以使用

text.format(**locals(),**globals())

在字符串中包含全局变量。

我刚刚为 f-string 编写了一个后向端口编译器,名为 f2format. Just as you requests, you may write f-string literals in Python 3.6 flavour, and compile to a compatible version for end-users to run, just like Babel 用于 JavaScript。

f2format 提供了一种智能但不完善的 后向端口编译器 解决方案。它应将 f-string 文字替换为 str.format 方法,同时保持源代码的原始布局。您可以简单地使用

f2format /path/to/the/file_or_directory

这将重写所有 Python 文件。例如,

var = f'foo{(1+2)*3:>5}bar{"a", "b"!r}boo'

将转换为

var = ('foo{:>5}bar{!r}boo').format(((1+2)*3), ("a", "b"))

字符串连接、转换、格式规范、多行和 unicode 都被正确处理。此外,f2format 将存档原始文件以防出现任何语法错误。

使用 simpleeval

的肮脏解决方案
import re
import simpleeval
test='_someString'
lst = ['_456']

s = '123123{lst[0]}{test}'

def template__format(template, context=None):
    if context is None:
        frame = inspect.currentframe()
        context = frame.f_back.f_locals        
        del frame
    ptn =  '([^{]?){([^}]+)}'
    class counter():
        i = -1

    def count(m):
        counter.i += 1
        return m.expand('\1{%d}'%counter.i)

    template = re.sub(ptn,string=s, repl= count)
    exprs = [x[1] for x in re.findall(ptn,s)]
    vals = map(simpleeval.SimpleEval(names=context).eval,exprs)
    res = template.format(*vals)
    return res

print (template__format(s))

我使用 'str'.format(**locals()) 有一段时间了,但过了一段时间才做这个,因为每个语句的附加代码有点麻烦

def f(string):
    """
    Poor man's f-string for older python versions
    """
    import inspect

    frame = inspect.currentframe().f_back

    v = dict(**frame.f_globals)
    v.update(**frame.f_locals)

    return string.format(string, **v)

# Example
GLOBAL = 123


def main():
    foo = 'foo'
    bar = 'bar'

    print(f('{foo} != {bar} - global is {GLOBAL}'))


if __name__ == '__main__':
    main()

使用 dict() 保存名称-值对

  • 除了本线程其他地方提到的方法(例如 format(**locals()) ),开发人员还可以创建一个或多个 python 字典来保存名称-值对。

  • 对于任何有经验的 python 开发人员来说,这是一个显而易见的方法,但很少有讨论明确列举这个选项,也许是因为它是如此明显的方法。

  • 相对于 locals() 的不加选择的使用,这种方法可以说是有利的,特别是因为它不那么不加选择。它明确使用一个或多个字典和命名空间来与您的格式化字符串一起使用。

  • Python 3 还允许解包多个字典(例如,.format(**dict1,**dict2,**dict3) ...这在 python 2.7 中不起作用)

  ## init dict
  ddvars = dict()

  ## assign fixed values
  ddvars['firname']   =   'Huomer'
  ddvars['lasname']   =   'Huimpson'
  ddvars['age']       =   33
  pass

  ## assign computed values
  ddvars['comname']   =   '{firname} {lasname}'.format(**ddvars)
  ddvars['reprself']  =   repr(ddvars)
  ddvars['nextage']   =   ddvars['age'] + 1
  pass

  ## create and show a sample message
  mymessage = '''
  Hello {firname} {lasname}!
  Today you are {age} years old.
  On your next birthday you will be {nextage} years old!
  '''.format(**ddvars)

  print(mymessage)