Интерполяция строк на Python

[EDIT 00]: я редактировал несколько раз сообщение и теперь даже название, пожалуйста, прочитайте ниже.

Я только что узнал о методе строковых форматов и его использовании со словарями, таких как vars(), locals() и globals(), например:

name = 'Ismael'
print 'My name is {name}.'.format(**vars())

Но я хочу:

name = 'Ismael'
print 'My name is {name}.' # Similar to ruby

Итак, я придумал это:

def mprint(string='', dictionary=globals()):
    print string.format(**dictionary)

Вы можете взаимодействовать с кодом здесь: http://labs.codecademy.com/BA0B/3#:workspace

Наконец, мне бы хотелось, чтобы функция была в другом файле с именем my_print.py, поэтому я мог сделать:

from my_print import mprint

name= 'Ismael'
mprint('Hello! My name is {name}.')

Но как сейчас, есть проблема с областями, как я могу получить пространство имен основного модуля в качестве словаря из импортированной функции mprint. (а не от my_print.py)

Надеюсь, я заставил себя удержаться, если нет, попробуйте импортировать функцию из другого модуля. (трассировка находится в ссылке)

Он обращается к globals() dict от my_print.py, но, конечно, имя переменной не определено в этой области, любые идеи о том, как это сделать?

Функция работает, если она определена в том же модуле, но заметьте, как я должен использовать globals(), потому что если бы я не получил только словарь со значениями в пределах mprint().

Я попытался использовать нелокальную и точечную нотацию для доступа к основным переменным модуля, но я все еще не могу понять.


[РЕДАКТИРОВАТЬ 01]: Думаю, я понял решение:

В my_print.py:

def mprint(string='',dictionary=None):
    if dictionary is None:
        import sys
        caller = sys._getframe(1)
        dictionary = caller.f_locals
    print string.format(**dictionary)

В test.py:

from my_print import mprint

name = 'Ismael'
country = 'Mexico'
languages = ['English', 'Spanish']

mprint("Hello! My name is {name}, I'm from {country}\n"
       "and I can speak {languages[1]} and {languages[0]}.")

Он печатает:

Hello! My name is Ismael, I'm from Mexico
and I can speak Spanish and English.

Как вы думаете, ребята? Это было сложно для меня!

Мне это нравится, гораздо читабельнее для меня.


[EDIT 02]: Я создал модуль с функцией interpolate, класс interpolate и попытку для метода класса interpolate, аналогичного функции.

Он имеет небольшой набор тестов и документирован!

Я застрял в реализации метода, я не понимаю.

Здесь код: http://pastebin.com/N2WubRSB

Как вы думаете, ребята?


[ИЗМЕНИТЬ 03]: Хорошо. Я решил использовать только функцию interpolate().

В string_interpolation.py:

import sys


def get_scope(scope):
    scope = scope.lower()
    caller = sys._getframe(2)
    options = ['l', 'local', 'g', 'global']

    if scope not in options[:2]:
        if scope in options[2:]:
            return caller.f_globals
        else:
            raise ValueError('invalid mode: {0}'.format(scope))
    return caller.f_locals


def interpolate(format_string=str(),sequence=None,scope='local',returns=False):
    if type(sequence) is str:
        scope = sequence
        sequence = get_scope(scope)
    else:
        if not sequence:
            sequence = get_scope(scope)

    format = 'format_string.format(**sequence)'
    if returns is False:
        print eval(format)

    elif returns is True:
        return eval(format)

Еще раз спасибо ребятам! Любые мнения?


[EDIT 04]:

Это моя последняя версия, у нее есть тест, docstrings и описаны некоторые ограничения, которые я нашел: http://pastebin.com/ssqbbs57

Вы можете быстро протестировать код здесь: http://labs.codecademy.com/BBMF#:workspace

И клонировать grom git repo здесь: https://github.com/Ismael-VC/python_string_interpolation.git

Ответ 1

Языковой дизайн - это не просто решение головоломок: ;)

http://www.artima.com/forums/flat.jsp?forum=106&thread=147358

Изменить: PEP-0498 решает эту проблему!

Класс Template из модуля string также выполняет то, что мне нужно (но больше похоже на метод строки format), в конце он также имеет читаемость, которую я ищу, он также имеет рекомендуемую явную, он находится в стандартной библиотеке, а также может быть легко настроен и расширен.

http://docs.python.org/2/library/string.html?highlight=template#string.Template

from string import Template

name = 'Renata'
place = 'hospital'
job = 'Dr.'
how = 'glad'
header = '\nTo Ms. {name}:'

letter = Template("""
Hello Ms. $name.

I'm glad to inform, you've been
accepted in our $place, and $job Red
will ${how}ly recieve you tomorrow morning.
""")

print header.format(**vars())
print letter.substitute(vars())

Самое смешное, что теперь я больше люблю использовать {} вместо $, и мне по-прежнему нравится модуль string_interpolation, с которым я столкнулся, потому что он меньше печатает, чем один в конечном итоге, LOL!

Запустите код здесь:

http://labs.codecademy.com/BE3n/3#:workspace

Ответ 2

Модули не разделяют пространства имен в python, поэтому globals() для my_print всегда будет globals() файла my_print.py; в том месте, где фактически была определена функция.

def mprint(string='', dic = None):
    dictionary = dic if dic is not None else globals()
    print string.format(**dictionary)

Вы должны передать текущие глобальные модули модуля() явно, чтобы заставить его работать.

Ans не используют изменяемые объекты в качестве значений по умолчанию в функциях python, это может привести к неожиданным результатам. Вместо этого используйте None как значение по умолчанию.

Простой пример для понимания областей в модулях:

file: my_print.py

x = 10
def func():
    global x
    x += 1
    print x

file: main.py

from my_print import *
x = 50
func()   #prints 11 because for func() global scope is still 
         #the global scope of my_print file
print x  #prints 50

Ответ 3

Часть вашей проблемы - ну, причина ее не работает - выделена в этом вопросе.

Вы можете использовать свою функцию, передав globals() в качестве второго аргумента mprint('Hello my name is {name}',globals()).

Хотя это может быть удобно в Ruby, я бы посоветовал вам не писать Ruby на Python, если вы хотите максимально использовать язык.