python - Python 中的递归字符串替换和转义

标签 python string recursion escaping formatter

我用 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/

相关文章:

python - 如何模拟 googleapiclient.discovery.build

python - 使用 json.loads 将字符串转换为字典

python - 使用 Python 和 RegEx 从多个 .txt 文件中提取某些数据

python - django 请求混淆了?

PHP preg_split : Split string by forward slash

const char * VS char const * const(不谈什么是const)

python - 检查大小写和句号的程序

algorithm - 斐波那契 : non-recursive vs memoized recursive puzzling timing results

javascript - 从嵌套的 javascript 对象中删除属性的最佳方法是什么?

algorithm - 这个递归算法的阶数/递归公式/闭合公式是什么?