Моя цель - создать очень простой язык шаблонов. На данный момент я работаю над заменой переменной на значение, например:
Этот ввод:
The Web
Должен произвести такой вывод:
The Web This Is A Test Variable
У меня это работает. Но, глядя на свой код, я запускаю несколько одинаковых регулярных выражений на одних и тех же строках, что просто оскорбляет мое чувство эффективности. Должен быть лучший, более питонический способ. (На самом деле оскорбляют два цикла while.)
Он проходит модульные тесты, поэтому, если это глупая преждевременная оптимизация, скажите мне - я готов отказаться от этого. В документе могут быть десятки определений и вариантов использования этих переменных, но не сотни. Но я подозреваю, что есть очевидные (для других) способы улучшить это, и мне любопытно, что придумает толпа StackOverflow.
def stripMatchedQuotes(item):
MatchedSingleQuotes = re.compile(r"'(.*)'", re.LOCALE)
MatchedDoubleQuotes = re.compile(r'"(.*)"', re.LOCALE)
item = MatchedSingleQuotes.sub(r'', item, 1)
item = MatchedDoubleQuotes.sub(r'', item, 1)
return item
def processVariables(item):
VariableDefinition = re.compile(r'<%(.*?)=(.*?)%>', re.LOCALE)
VariableUse = re.compile(r'<%(.*?)%>', re.LOCALE)
Variables = {}
while VariableDefinition.search(item):
VarName, VarDef = VariableDefinition.search(item).groups()
VarName = stripMatchedQuotes(VarName).upper().strip()
VarDef = stripMatchedQuotes(VarDef.strip())
Variables[VarName] = VarDef
item = VariableDefinition.sub('', item, 1)
while VariableUse.search(item):
VarName = stripMatchedQuotes(VariableUse.search(item).group(1).upper()).strip()
item = VariableUse.sub(Variables[VarName], item, 1)
return item
@Schof: позвольте мне указать вам на genshi.edgewall.org, который сделает вашу жизнь пирогом.
Мне нравится Title +1 за то, что заставляет меня смеяться.





Никогда не создавайте свой собственный язык программирования. Всегда. (Раньше у меня было исключение из этого правила, но теперь его нет.)
Всегда есть язык, который вы можете использовать, который лучше соответствует вашим потребностям. Если вы подробно остановились на своем сценарии использования, люди могут помочь вам выбрать подходящий язык.
Создание предметно-ориентированного языка - это совершенно нормально, если это уместно. Никогда не говори никогда.
Может быть, но уже есть миллион хороших вариантов для языков шаблонов текста в Python ...
Отчасти я делаю это для того, чтобы повысить свои навыки. Если бы мне за это платили, я бы последовал вашему совету и сделал бы это как можно быстрее. Я стараюсь максимизировать обучение, а не эффективность программиста.
Создание языка для целей самообразования на самом деле было одним из упомянутых мной исключений. Что касается DSL: вы часто можете избежать этого, реализовав вместо этого хорошо спроектированную библиотеку классов или API.
Вы можете сопоставить оба типа кавычек за один раз с r"(\"|')(.*?)" - относится к первой группе, поэтому он будет соответствовать только совпадающим кавычкам.
Не вызывайте поиск дважды подряд (в условном цикле и в первом операторе цикла). Вызов (и кеширование результата) один раз перед циклом, а затем в последнем операторе цикла.
Вы довольно часто вызываете re.compile. Глобальная переменная для них здесь не помешает.
Первое, что может улучшить ситуацию, - это переместить re.compile за пределы функции. Компиляция кэшируется, но при ее проверке, чтобы увидеть, скомпилирована ли она, требуется меньшая скорость.
Другая возможность - использовать одно регулярное выражение, как показано ниже:
MatchedQuotes = re.compile(r"(['\"])(.*)", re.LOCALE)
item = MatchedQuotes.sub(r'', item, 1)
Наконец, вы можете объединить это в регулярное выражение в processVariables. Принимая предложение Торстена Марека использовать функцию для re.sub, это значительно улучшает и упрощает ситуацию.
VariableDefinition = re.compile(r'<%(["\']?)(.*?)=(["\']?)(.*?)%>', re.LOCALE)
VarRepl = re.compile(r'<%(["\']?)(.*?)%>', re.LOCALE)
def processVariables(item):
vars = {}
def findVars(m):
vars[m.group(2).upper()] = m.group(4)
return ""
item = VariableDefinition.sub(findVars, item)
return VarRepl.sub(lambda m: vars[m.group(2).upper()], item)
print processVariables('<%"TITLE" = "This Is A Test Variable"%>The Web <%"TITLE"%>')
Вот мои тайминги для 100000 пробежек:
Original : 13.637
Global regexes : 12.771
Single regex : 9.095
Final version : 1.846
[Edit] Добавить отсутствующий нежадный спецификатор
[Edit2] Добавлены вызовы .upper () без учета регистра, как в оригинальной версии
sub может принимать вызываемый объект в качестве аргумента, а не простую строку. Используя это, вы можете заменить все переменные одним вызовом функции:
>>> import re
>>> var_matcher = re.compile(r'<%(.*?)%>', re.LOCALE)
>>> string = '<%"TITLE"%> <%"SHMITLE"%>'
>>> values = {'"TITLE"': "I am a title.", '"SHMITLE"': "And I am a shmitle."}
>>> var_matcher.sub(lambda m: vars[m.group(1)], string)
'I am a title. And I am a shmitle.
Следуйте совету eduffy.myopenid.com и храните скомпилированные регулярные выражения.
Тот же рецепт можно применить к первому циклу, только там вам нужно сначала сохранить значение переменной и всегда возвращать "" в качестве замены.
Если регулярное выражение содержит только один подстановочный знак. * И литералы, вы можете использовать find и rfind, чтобы найти открывающий и закрывающий разделители.
Если он содержит только серию. *? подстановочные знаки и литералы, тогда вы можете просто использовать серию находок для выполнения работы.
Если код критичен ко времени, этот отказ от регулярных выражений может дать немного больше скорости.
Кроме того, мне кажется, что это LL-анализируемый язык. Вы можете поискать библиотеку, которая уже может анализировать такие вещи за вас. Вы также можете использовать рекурсивные вызовы для выполнения однопроходного синтаксического анализа - например, вы можете реализовать свою функцию processVariables, чтобы использовать только первую цитату, а затем вызвать функцию сопоставления цитат для использования до следующей цитаты и т. д.
Создание языка шаблонов - это хорошо, но разве не должна быть одной из целей языка шаблонов легкость чтения и эффективный синтаксический анализ? Пример, который вы привели, похоже, ни то, ни другое.
Как сказал Джейми Завински:
Some people, when confronted with a problem, think "I know, I'll use regular expressions!" Now they have two problems.
Если регулярные выражения - это решение созданной вами проблемы, лучше всего не писать более качественное регулярное выражение, а изменить свой подход, чтобы полностью исключить их использование. Регулярные выражения сложны, дороги, чрезвычайно трудны в обслуживании и (в идеале) должны использоваться только для решения проблемы, созданной кем-то другим.
В принципе согласен. Мы должны пойти в uservoice и заставить их показывать эту цитату каждый раз, когда кто-то пишет вопрос и присваивает ему тег «regex».
Мне любопытно, почему вы думаете, что в этом примере нет ни удобочитаемости, ни эффективного синтаксического анализа? Что я могу изменить, чтобы сделать его более читаемым и легким для анализа?
Почему бы не использовать XML и XSLT вместо создания собственного языка шаблонов? В XSLT сделать то, что вы хотите, довольно просто.
Почему бы не использовать Мако? Серьезно. Какая функция вам нужна, которой нет у Mako? Возможно, вы сможете адаптировать или расширить то, что уже работает.
Миру не нужен другой язык шаблонов Python. Их уже десятки. Разве ты не можешь просто использовать один из них?