【问题标题】: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)导入旧的 3.x、2.x Python 吗?
【发布时间】:2017-06-25 02:56:13
【问题描述】:

对我来说,新的 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 string f-string


    【解决方案1】:

    future-fstrings 将 f 字符串带入 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}')
    

    【讨论】:

    • 不幸的是,这破坏了 emacs:Warning (mule): Invalid coding system future_fstrings' 被指定`
    • 如果你删除 -*- 位,Python 仍然会识别它。
    • 我只是用它为我做自动代码转换:安装后future-fstrings-show main.py。然后我复制/粘贴代码。这有效,而我无法在 jupyter 中“开箱即用”工作(不确定是否打算这样做)。但是,如果您想要的只是自动代码转换,您可能可以 DIY。
    • 您可以尝试在 py 脚本的开头添加,而不是在顶部添加编码字符串:python import future_fstrings future_fstrings.register()
    【解决方案2】:

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

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

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

    【讨论】:

    • 有什么理由不使用下面的答案中提到的包“future-fstrings”吗?我感谢您在任何情况下回答此问题时可能无法使用此选项。
    • @Siwel 这是一个非常聪明的反向移植方法。我认为没有任何严重的理由不使用它。但是,使用自定义编码可能会与某些 IDE 发生冲突。我不认为它反对PEP 263 Defining Python Source Code Encodings,但这并不是“编码线”的真正用例。不过,这是一个非常聪明的方法,我一定会在未来对其进行测试。
    • format_map 如果通过了locals() 变量字典,这可能是一个有用的解决方法......我自己还没有测试过,但它应该可以工作
    【解决方案3】:

    这是我使用的:

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

    它解压(**locals() 返回的字典,其中包含所有局部变量作为字典 {variable_name: value}

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

    你也可以使用

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

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

    【讨论】:

    • 这是可行的,但我认为应该避免这种“黑客行为”!
    • 如果字符串是由外部来源提供的,这是有风险的
    【解决方案4】:

    解释器在标记 f 前缀时创建 f 字符串 - 仅此功能就会扼杀任何兼容性机会。

    您最接近的方法是使用关键字格式,例如

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

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

    【讨论】:

    • 如果使用这种方法,并且假设年龄和单位已经是变量,那么对于 python2.7 将其写为'Foo is {age} {units} old'.format(age=age, units=units) 可能更有意义,因此可以快速将其更新为f'Foo is {age} {units} old'迁移到 python3.6 时
    • 'Foo is {age} {units} old'.format(**locals(), **globals()) 呢?
    • @MadPhysicist ,工作但被认为是错误的形式,因为在格式调用中包含厨房水槽。
    • @GringoSuave。同意,但这与实际 f 字符串的评估方式非常接近。
    • @MadPhysicist 并不是这样的明确设计目标。字符串在编译时被解析为字符串和表达式部分,表达式部分是普通的 Py 代码。所以只有被命名的变量被引用,而不是整个命名空间。有点理论,但与众不同。
    【解决方案5】:

    我刚刚为 f-string 编写了一个后端编译器,名为 f2format。正如您的要求,您可以用 Python 3.6 风格编写 f-string 文字,并编译成兼容版本供最终用户运行,就像 Babel 一样JavaScript。

    f2format 提供了一个智能但不完美的反向移植编译器解决方案。它应该用 str.format 方法替换 f-string 文字,同时保持源代码的原始布局。你可以简单地使用

    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 将归档原始文件,以防出现任何语法违规。

    【讨论】:

    • 我希望它是开源的,因为我不会让随机程序运行在我的源代码上:(
    • @Walter 是的,它在 Apache License 2.0 下是开源的;只需查看repo :)
    【解决方案6】:

    我已经使用'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()
    

    【讨论】:

      【解决方案7】:

      使用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))
      
      

      【讨论】:

        【解决方案8】:

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

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

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

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

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

        ## 初始化字典 ddvars = dict() ##分配固定值 ddvars['firname'] = 'Huomer' ddvars['lasname'] = 'Huimpson' ddvars['年龄'] = 33 经过 ## 分配计算值 ddvars['comname'] = '{firname} {lasname}'.format(**ddvars) ddvars['reprself'] = repr(ddvars) ddvars['nextage'] = ddvars['age'] + 1 经过 ## 创建并显示示例消息 我的消息 = ''' 你好 {firname} {lasname}! 今天你 {age} 岁。 在您的下一个生日,您将满 {nextage} 岁! '''.format(**ddvars) 打印(我的消息)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-27
          • 1970-01-01
          • 2011-08-22
          • 1970-01-01
          • 2016-12-29
          相关资源
          最近更新 更多