我的目标是创建一个非常简单的模板语言。目前,我正在尝试使用值替换变量,如下所示:
此输入:
网络
应该产生这个输出:
网络这是一个测试变量
我已经开始工作了。但是看看我的代码,我在相同的字符串上运行多个相同的正则表达式 - 这只是冒犯了我的效率感。必须有更好,更Pythonic的方式。 (这是真正冒犯的两个“while”循环。)
这确实通过了单元测试,所以如果这是愚蠢的过早优化,请告诉我 - 我愿意放手。文档中可能有许多这些变量定义和用法,但不是数百个。但我怀疑(对其他人)显而易见的是改善这一点的方法,我很好奇StackOverflow人群会想出什么。
def stripMatchedQuotes(item):
MatchedSingleQuotes = re.compile(r"'(.*)'", re.LOCALE)
MatchedDoubleQuotes = re.compile(r'"(.*)"', re.LOCALE)
item = MatchedSingleQuotes.sub(r'\1', item, 1)
item = MatchedDoubleQuotes.sub(r'\1', 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
答案 0 :(得分:10)
可能改进的第一件事是将re.compile移到函数之外。编译是缓存的,但检查它是否有速度命中,以查看它是否已编译。
另一种可能性是使用单个正则表达式如下:
MatchedQuotes = re.compile(r"(['\"])(.*)\1", re.LOCALE)
item = MatchedQuotes.sub(r'\2', item, 1)
最后,您可以将其组合到processVariables中的正则表达式中。考虑Torsten Marek's建议使用re.sub函数,这可以显着改善和简化事物。
VariableDefinition = re.compile(r'<%(["\']?)(.*?)\1=(["\']?)(.*?)\3%>', re.LOCALE)
VarRepl = re.compile(r'<%(["\']?)(.*?)\1%>', 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
[编辑]添加缺少的非贪婪说明符
[Edit2]添加了.upper()调用,因此不区分大小写,如原始版本
答案 1 :(得分:3)
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的建议并保留已编译的正则表达式。
同一个配方可以应用于第一个循环,只需要先存储变量的值,并始终返回""
作为替换。
答案 2 :(得分:2)
永远不要创建自己的编程语言。永远。 (我曾经对此规则有例外,但不再有。)
您可以使用现有的语言,更好地满足您的需求。如果您详细说明了您的用例,人们可能会帮助您选择合适的语言。
答案 3 :(得分:2)
创建模板语言一切都很好,但是模板语言的目标之一不应该是易读性和高效解析吗?你给出的例子似乎都不是。
正如Jamie Zawinsky所说:有些人在面对的时候 问题,想想“我知道,我会用 正则表达式!“现在他们有了 两个问题。
如果正则表达式是您创建的问题的解决方案,那么最好的选择不是编写更好的正则表达式,而是重新设计您的方法以完全消除它们的使用。正则表达式复杂,昂贵,难以维护,并且(理想情况下)仅应用于解决其他人创建的问题。
答案 4 :(得分:1)
您可以使用r"(\"|')(.*?)\1"
一次匹配两种报价 - \1
指的是第一组,因此它只匹配匹配的报价。
答案 5 :(得分:1)
你正在调用re.compile。这些全局变量在这里不会受到伤害。
答案 6 :(得分:1)
如果正则表达式只包含一个。*通配符和文字,那么您可以使用find和rfind来定位开始和结束分隔符。
如果它只包含一系列。*?通配符和文字,然后你可以使用一系列的find来完成这项工作。
如果代码对时间要求严格,那么完全脱离正则表达式可能会提高速度。
此外,我认为这是一个LL-parsable language。你可以找一个可以解析这些东西的库。您还可以使用递归调用来执行一次性解析 - 例如,您可以实现processVariables函数以仅消耗第一个引用,然后调用引用匹配函数以消耗下一个引用等。
答案 7 :(得分:1)
为什么不使用Mako?认真。 Mako没有你需要什么功能?也许你可以适应或扩展已经有效的东西。
答案 8 :(得分:0)
不要连续两次调用搜索(在循环条件中,以及循环中的第一个语句)。在循环之前调用(并缓存结果)一次,然后在循环的最后一个语句中调用。
答案 9 :(得分:-3)
为什么不使用XML和XSLT而不是创建自己的模板语言?在XSLT中,您想要做的事情非常简单。