python 中的级联字符串插值
Cascading string interpolation in python
给定一个格式字符串字典,
我想做 cascading/recursive 字符串插值。
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()
函数首先select格式化字符串。
然后,它会替换这些字符串,直到不再有格式字符串为止。
当我使用以下 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]]
您可以轻松地在 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]]
现在,如果存在引用循环,则会引发错误。这将检测任何长度的引用循环,因为它会被替换,直到找到一个或所有替换都完成。
如果您唯一的问题是检测循环引用以避免无限循环,您可以在一个插值 returns 其输入时立即停止:
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
给定一个格式字符串字典, 我想做 cascading/recursive 字符串插值。
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()
函数首先select格式化字符串。
然后,它会替换这些字符串,直到不再有格式字符串为止。
当我使用以下 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]]
您可以轻松地在 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]]
现在,如果存在引用循环,则会引发错误。这将检测任何长度的引用循环,因为它会被替换,直到找到一个或所有替换都完成。
如果您唯一的问题是检测循环引用以避免无限循环,您可以在一个插值 returns 其输入时立即停止:
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