【问题标题】:Cascading string interpolation in pythonpython中的级联字符串插值
【发布时间】:2016-06-14 12:20:41
【问题描述】:

给定一个格式字符串字典, 我想做级联/递归字符串插值。

FOLDERS = dict(home="/home/user",
               workspace="{home}/workspace",
               app_project="{workspace}/{app_name}",
               app_name="my_app")

我从这个实现开始:

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
        remain = [k for k in remain if "{" in attrs[k]]

interpolate() 函数首先选择格式字符串。 然后,它会替换字符串,直到不再有格式字符串。

当我使用以下 Python 字典调用此函数时,我得到:

>>> import pprint
>>> pprint.pprint(FOLDERS)
{'app_name': 'my_app',
 'app_project': '/home/user/workspace/my_app',
 'home': '/home/user',
 'workspace': '/home/user/workspace'}

结果没问题,但是这个实现没有检测到引用循环。

例如,下面的调用导致无限循环!

>>> interpolate({'home': '{home}'})

谁能给我一个更好的实现?

编辑:解决方案

我认为 Leon 的解决方案既好又简单,Serge Bellesta 也是。

我会这样实现:

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
            fmt = '{' + k + '}'
            if fmt in attrs[k]: # check for reference cycles
                raise ValueError("Reference cycle found for '{k}'!".format(k=k))
        remain = [k for k in remain if "{" in attrs[k]]

【问题讨论】:

  • “谁能给我一个更好的实现?” - 这不是 SO 的用途。您要解决的实际问题是什么——您真的会得到任何带有参考周期的输入吗?
  • “谁能给我一个更好的实现?” - 它只需要为给定的输入产生完全相同的结果吗?
  • 事实上,我正在寻找一个通用的解决方案。示例仅用于说明。是的,如果定义文件夹的用户在(通常)配置文件中出错,我们可以有循环:interpolate() 函数应该找到它。

标签: python string-interpolation string-substitution


【解决方案1】:

您可以在 for 循环中轻松检查此类引用循环。只需检查是否在 for 循环中的匹配值中引用了该键:

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
            if '{%s}' % k in attrs[k]: # check for reference cycles
                raise ValueError("Input contains at least one reference cycle!")
        remain = [k for k in remain if "{" in attrs[k]]

现在,如果存在引用循环,则会引发错误。这将检测任何长度的引用循环,因为它会被替换,直到找到一个或所有替换完成。

【讨论】:

    【解决方案2】:

    如果您唯一的问题是检测循环引用以避免无限循环,则可以在一个插值返回其输入时立即停止:

    def interpolate(attrs):
        remain = [k for k, v in attrs.items() if "{" in v]
        while remain:
            for k in remain:
                attrs[k] = attrs[k].format(**attrs)
            temp = [k for k in remain if "{" in attrs[k]]
            if temp == remain:
                # cyclic reference detected
                ...
            remain = temp
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多