Могу ли я импортировать отформатированные строковые литералы Python 3.6 (f-строки)

Новые f-строки Python 3.6 кажутся мне огромным скачком в удобстве и простоте использования строк, и я бы с удовольствием включился и безоговорочно принял их в новых проектах, которые могут работать на старых интерпретаторах. Поддержка 2.7, 3.3-3.5 была бы отличной, но по крайней мере я хотел бы использовать их в базах кода Python 3.5. Как я могу импортировать форматированные строковые литералы 3.6 для использования более старыми интерпретаторами?

Я понимаю, что форматированные строковые литералы, такие как f"Foo is {age} {units} old" не нарушать изменения, поэтому не будет включен в from __future__ import ... вызов. Но изменение не перенесено обратно (AFAIK). Мне нужно быть уверенным, что любой новый код, который я пишу с помощью f-строк, запускается только на Python 3.6+, который является преградой для многих проектов.

5 ответов

Решение

К сожалению, если вы хотите использовать его, вы должны требовать Python 3.6+то же самое с оператором умножения матриц @ а также Python 3.5+ или же yield from (Python 3.4+ Я думаю)

Они внесли изменения в интерпретацию кода и, таким образом, генерируют ошибки синтаксиса при импорте в более старые версии. Это означает, что вам нужно поместить их куда-нибудь, где они не импортируются в старых питонах или не охраняются eval или же exec (Я бы не рекомендовал последние два!).

Так что да, вы правы, если вы хотите поддерживать несколько версий Python, вы не можете легко их использовать.

future-fstrings приносит f-строки в скрипты Python 2.7. (И я предполагаю, что 3.3-3.5 основано на документации.)

После того, как вы установите pip через pip install future-fstringsВы должны поместить специальную строку в верхней части вашего кода. Эта строка:

# -*- coding: future_fstrings -*-

Затем вы можете использовать форматированные строковые литералы (f-строки) в вашем коде:

# -*- 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-строки создаются интерпретатором при маркировке f префикс - одна эта функция убьет все шансы на совместимость.

Ваш самый близкий выстрел должен использовать форматирование ключевого слова, как

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

который может быть легко реорганизован после прекращения требования совместимости.

Я только что написал бэк-порт компилятора для f-строки, под названием f2format, Точно так же, как вы запрашиваете, вы можете писать литералы f-строки в Python 3.6 и компилировать в совместимую версию для запуска конечными пользователями, как Babel для JavaScript.

f2format предоставляет интеллектуальное, но несовершенное решение компилятора с обратным портом. Он должен заменить f-строковые литералы на 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"))

Конкатенация строк, преобразование, спецификация формата, многострочные и unicodes все обрабатываются правильно. Также, f2format архивирует исходные файлы в случае нарушения синтаксиса.

Я использовал '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()

  ## присвоить фиксированные значения
  ddvars['firname']   =   'Хуомер'
  ddvars ['lasname'] = 'Хуимпсон'
  ddvars ['age'] = 33
  проходить

  ## присвоить вычисленные значения
  ddvars['comname']   =   '{firname} {lasname}'. формат (**ddvars)
  ddvars['reprself'] = repr(ddvars)
  ddvars['nextage'] = ddvars['age'] + 1
  проходить

  ## создать и показать образец сообщения
  mymessage = '''
  Здравствуйте, {firname} {lasname}!
  Сегодня вам {age} лет.
  В следующий день рождения вам исполнится {nextage} лет!
  '' '.format (**ddvars)

  печать (мое сообщение)

Грязное решение с использованием 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))

Другие вопросы по тегам