我正在尝试使用以下函数包装API。 API的端点与此类似:
/users/{ids}
/users/{ids}/permissions
我的想法是,我将能够将字典传递给包含ids
列表的函数,并将其格式化为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)
这有效,但我无法想象if
语句是有效的。
如何改进它并让它在Python 2.7和Python 3.5中都有效?
我也被告知在迭代时更改字典很糟糕,但在我的测试中我从来没有遇到过问题。我pop
这个值,因为我后来检查是否有意外的参数(即data
中剩下的任何内容)。我现在正在以正确的方式做什么?
答案 0 :(得分:1)
这是做到这一点的方法。首先,解析字符串的键。然后它会记住网址中未使用的所有密钥并将其保存在侧面。最后,它使用dict的给定参数格式化url。该函数返回未使用的变量和格式化的URL。如果您希望通过迭代它们并从dict中删除,可以从dict中删除未使用的变量。 这里有一些文档,其中包含有关format syntax的示例。
import string
users = {'ids': [1, 2, 3, 5]}
def call_api(url, data):
data_set = set(data)
formatter = string.Formatter()
used_set = {f[1] for f in formatter.parse(url) if f[1] is not None}
unused_set = data_set - used_set
formatted = url.format(**{k: ";".join(str(x) for x in v)
for k, v in data.items()})
return unused_set, formatted
print(call_api('/users/{ids}/permissions', users))
答案 1 :(得分:1)
您可以使用re.subn返回替换次数:
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
进行破坏性迭代时创建项列表的副本。
修改强>:
似乎主要的瓶颈是检查密钥是否在网址中。 in
运算符是执行此操作的最有效方法,并且比正在使用的简单模式的正则表达式快得多。分别记录未使用的密钥也比破坏性迭代更有效,但它并没有产生那么大的差异(相对而言)。
所以:原始解决方案没有太大问题,但@wegry给出的解决方案效率最高。
答案 2 :(得分:1)
不是在迭代时修改字典,而是创建另一个对象以保存未使用的密钥,这可能是要走的路。至少在Python 3.4+中,在迭代期间删除键会引发一个
RuntimeError: dictionary changed size during iteration
。
def call_api(url, data):
unused_keys = set()
for k, value in data.items():
key_pattern = "{" + k + "}"
if key_pattern in url:
formatted_value = ';'.join(map(str, value))
url = url.replace(key_pattern, formatted_value)
else:
unused_keys.add(k)
此外,如果您认为自己更有可能遇到未使用的密钥,则可能需要撤消条件。
答案 3 :(得分:1)
可以使用RegEx找到格式化键,然后将其与字典中的键进行比较。您的字符串已设置为使用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'})
这不是那么好,但它很简洁。正如其中一条评论所述,这种方法似乎不太可能成为瓶颈。