我用 Python 编写了一个简单的脚本来根据源目录结构生成一些项目。在其中我使用了Formatter
,因为事实证明它非常方便,能够使用字典(也是嵌套的!)来替换字符串。
但是现在,在扩展脚本的同时,我需要一个更复杂的替换。首先,我希望替换是递归的。替换字段的字符串可能需要自行格式化(使用相同的参数)。其次,我需要能够根据提供的函数转义最终字符串(到目前为止唯一的用例是 re.escape
来转义正则表达式)。
我在 Python 中寻找内置的东西,但没有找到任何有用的东西。 Formatter
(如提供的)显然不符合这些标准。
我的第一次尝试是使用一个简单的函数,例如:
def expand_vars(string, vars):
while True:
expanded = string.format(**vars)
if expanded == string:
break
string = expanded
return string
它只是不断调用format
,直到字符串不再发生任何变化(这意味着所有字段都被替换)。
然而,在这里嵌入转义并不容易。我只需要转义替换值(不是整个字符串
)和最终值(每次调用转义都会导致多次转义字符串的某些部分)。
此函数的另一个问题是它可能会无意中创建不是字段的字段。在下一次迭代中,当其中一个字段以字符串 {a
结尾,而另一个字段以 b}
结尾时,我们会得到意外的字段 {ab}
。 (嗯,它可以被认为是一个功能,但在我的例子中我并没有这样认为。)
另一种方法是子类化Formatter
。我最终得到了类似的结果:
class RecursiveEscapingFormatter(Formatter):
def __init__(self, escape=None):
Formatter.__init__(self)
self.escape = escape
def get_field(self, field_name, args, kwargs):
obj, arg_used = super(RecursiveEscapingFormatter, self).get_field(field_name, args, kwargs)
if self.escape is None:
nonEscapingFormatter = self
else:
nonEscapingFormatter = copy.copy(self);
nonEscapingFormatter.escape = None
obj = nonEscapingFormatter.vformat(obj, args, kwargs)
return obj, arg_used
def convert_field(self, value, conversion):
result = super(RecursiveEscapingFormatter, self).convert_field(value, conversion)
if self.escape is not None:
result = self.escape(result)
return result
现在的问题是我无法确保正确调用check_unused_args
。我看不出有什么方法可以合理地(=不需要覆盖整个类)跟踪 get_field
中递归调用使用的参数。我自己不需要这个,但创建一个合适的类(一个稍后可以从...继承的类)需要正确处理 check_unused_args
。如何做到这一点?
或者也许有更好的方法来解决这个问题(用转义进行递归替换)?
最佳答案
我遇到了类似的问题,以下是我的解决方法。
from string import Formatter
class RecursivelySubstitutedDictionary:
def __init__(self, dictionary):
self.formatter = Formatter()
self.dictionary = dictionary
self.substituting = set([])
def __getitem__(self, key):
if(key in self.substituting):
raise ValueError("Cyclic reference. Key: %s." % key)
self.substituting.add(key)
unsubstitutedval = self.dictionary[key]
substitutedval = self.formatter.vformat(unsubstitutedval,[],self)
self.substituting.remove(key)
return substitutedval
使用示例
regulardict = {
'hi': 'hello {arg}',
'arg': '{arg_1}{arg_2}',
'arg_1': 'wo',
'arg_2': 'rld',
}
print RecursivelySubstitutedDictionary(regulardict)['hi']
# prints hello world
cyclicdict = {
'hi': 'hello {arg}',
'arg': '{hi}',
}
print RecursivelySubstitutedDictionary(cyclicdict)['hi']
# raises ValueError: Cyclic reference. Key: hi.
如果调用__setitem__
,您还可以考虑缓存替换的值并清除缓存。至少这就是我在原始代码中所做的。
关于python - Python 中的递归字符串替换和转义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24986679/