替换字符串中关键字的更具Python风格的方法?

替换字符串中关键字的更具Python风格的方法?,python,Python,我试图用以下函数包装一个API。API的端点与此类似: /users/{ids} /users/{ids}/permissions 我的想法是,我将能够向我的函数传递一个包含ID列表的字典,这些ID将按照API的预期进行格式化: users = {'ids': [1, 2, 3, 5]} call_api('/users/{ids}/permissions', users) 然后在call_api中,我现在做了类似的事情 def call_api(url, data): for k,

我试图用以下函数包装一个API。API的端点与此类似:

/users/{ids}
/users/{ids}/permissions
我的想法是,我将能够向我的函数传递一个包含ID列表的字典,这些ID将按照API的预期进行格式化:

users = {'ids': [1, 2, 3, 5]}
call_api('/users/{ids}/permissions', users)
然后在call_api中,我现在做了类似的事情

def call_api(url, data):
    for k, value in data.items():
        if "{" + key + "}" in url:      
            url = url.replace("{"+k+"}", ';'.join(str(x) for x in value))
            data.pop(k, None)
这是可行的,但我无法想象如果语句是有效的

如何改进它并使其在Python2.7和Python3.5中工作


我还被告知,在迭代时更改字典是不好的,但在我的测试中,我从来没有遇到过问题。我正在弹出该值,因为我稍后会检查是否有意外参数,即数据中是否有任何剩余的参数。我现在所做的是正确的吗?

以下是方法。首先,为键解析字符串。然后,它会记住url中未使用的所有键,并将其保存在侧边。最后,它使用dict的给定参数格式化url。该函数返回未使用的变量和格式化的url。如果您愿意,可以通过迭代并从dict中删除未使用的变量来从dict中删除这些变量。 下面是一些文档,其中包含了有关的示例

您可以使用返回替换次数的:

import re

def call_api(url, data):
    for k, value in list(data.items()):
        url, n = re.subn(r'\{%s\}' % k, ';'.join(str(x) for x in value), url)
        if n:
            del data[k]
请注意,为了与python2和python3兼容,在对dict进行破坏性迭代时,还需要创建项目列表的副本

编辑:

似乎主要的瓶颈是检查密钥是否在url中。in操作符是实现这一点的最有效的方法,对于这里使用的简单模式,它比正则表达式快得多。单独记录未使用的键也比破坏性迭代更有效,但相对而言,它不会产生太大的差异


因此:原始解决方案没有太大问题,但是@wegry给出的解决方案是最有效的。

与其在迭代字典时修改它,不如创建另一个对象来保存未使用的键。至少在Python3.4+中,在迭代过程中删除键会引起一个错误 运行时错误:字典在迭代期间更改了大小


此外,如果您认为更有可能遇到未使用的键,则可以反转条件。

可以使用正则表达式找到格式化键,然后与字典中的键进行比较。您的字符串已设置为使用str.format,因此可以对数据中的值应用转换,然后应用该转换

import re
from toolz import valmap

def call_api(url, data):
    unused = set(data) - set(re.findall('\{(\w+)\}', url))
    url = url.format_map(valmap(lambda v: ';'.join(map(str, v)), data))
    return url, unused
用法如下所示:

users = {'ids': [1, 2, 3, 5], 'unused_key': 'value'}
print(call_api('/users/{ids}/permissions', users))
# ('/users/1;2;3;5/permissions', {'unused_key'})

这个时间不太合适,但它很简洁。正如其中一条评论中所指出的,这种方法似乎不太可能成为瓶颈。

if语句似乎适用于可能相对较少的键。您可以尝试通过使用正则表达式来提取{和}之间的任何值来确定值,但是如果您的数据只有几个键,就不用麻烦了。我看不出你有任何理由修改数据值。你为什么要弹出?我没有显示完整的函数@AustinHastings,但是在函数的后面我需要看看这些键是否是未知键——那些没有弹出的键。我有额外的逻辑来处理剩余的键/值。对于10个键,总共大约有10个键,尽可能地降低效率。您将要调用web API,此代码不会成为您的性能瓶颈-你用什么来提供这项服务?如果是django,它就有处理这些参数的方法。Flask也有这个选项。这个解决方案会破坏OPs代码,因为当url不包含键时,它会从数据中删除项。如果检查是必要的,因为OP想要检测意外的参数。我已经修复了代码。解决方案现在将检测意外参数,并替换所有现有参数。请记住,OP希望跟踪未转换的单词。
import re
from toolz import valmap

def call_api(url, data):
    unused = set(data) - set(re.findall('\{(\w+)\}', url))
    url = url.format_map(valmap(lambda v: ';'.join(map(str, v)), data))
    return url, unused
users = {'ids': [1, 2, 3, 5], 'unused_key': 'value'}
print(call_api('/users/{ids}/permissions', users))
# ('/users/1;2;3;5/permissions', {'unused_key'})