我正在编写一个脚本来将ColdFusion CFML代码转换为CFScript代码。在许多地方,它将采用属性字典,查看函数列表,并调用第一个参数与给定属性匹配的字典作为关键字args:
import inspect
def invokeFirst(attributes, *handlers):
given_args = set(attributes)
for handler in handlers:
meta = inspect.getargspec(handler)
allowed_args = set(meta.args)
required_args = set(meta.args[:-len(meta.defaults)]) if meta.defaults else meta.args
if required_args <= given_args and (meta.keywords or given_args <= allowed_args):
return handler(**attributes)
raise TypeError("Can't invoke with arguments {}.".format(str(given_args)))
使用示例:
def replaceLoop(tag):
forIn = 'for (var {item} in {collection}) {{'
return invokeFirst(tag.attributes,
lambda item, collection: forIn.format( item=bare(item) , collection=collection ),
lambda index, array : forIn.format( item=bare(index), collection=index ),
lambda _from, to, index:
'for (var {index} = {min}; {index} <= {max}; {index}++) {{'.format(
index=bare(index), min=_from, max=to,
),
)
现在,由于from
不是有效的参数名称,我必须在lambda中为它添加前缀,并向invokeFirst
添加一堆额外的逻辑(未显示)。是否有更简单的解决方法在使用时不会使语法膨胀?
答案 0 :(得分:1)
这可能对您的用例来说太简单了(或者您甚至可能会考虑这个&#34;在使用时膨胀语法&#34;);但是你能依靠Python的EAFP原则:尝试调用函数,而忽略任何异常吗?类似的东西:
def invokeFirst(attributes, *handlers):
for handler in handlers:
try:
val = handler(attributes)
return val
except (KeyError, TypeError):
pass
raise TypeError("Can't invoke with arguments {}.".format(str(attributes)))
def replaceLoop(tag):
forIn = 'for (var {item} in {collection}) {{'
return invokeFirst(tag.attributes,
lambda d: forIn.format( item=bare(d['item']) , collection=d['collection'] ),
lambda d: forIn.format( item=bare(d['index']), collection=d['index'] ),
lambda d:
'for (var {index} = {min}; {index} <= {max}; {index}++) {{'.format(
index=bare(d['index']), min=d['from'], max=d['to'],
)
)
bare = lambda b: b
class Tag:
def __init__(self, dct):
self.attributes = dct
tag = Tag({'item':4})
print(replaceLoop(tag))
#satisfies 2nd function - outputs:
for (var 4 in 4) {