我在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
,直到字符串中没有任何更改(这意味着所有字段都被替换)。
然而,在这里嵌入逃脱并不容易。我只需要转义替换值(不是整个string
),只有最终值(每次调用时转义都会导致多次转义字符串的某些部分)。
此功能的另一个问题是它可能无意中创建非字段的字段。当其中一个字段以字符串{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
。怎么做?
或许是否有更好的方法解决问题(使用转义进行递归替换)?
答案 0 :(得分:2)
我遇到了类似的问题,这就是我如何解决它的问题。
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__
,您还可以考虑缓存替换的val并清除缓存。至少这是我在原始代码中所做的事情。