观看pycon的Raymond Hettinger演讲后,他演示了“循环执行”的更好方法
blocks = []
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
等于:
blocks = []
for block in iter(partial(f.read, 32), ''):
blocks.append(block)
代码中有相同的结构。但是,如果需要更改iter中的函数参数,那么它就不能“正确”地工作。
def get_data_from_user(user, type, token):
data = []
url = f'https://api.github.com/users/{user}/{type}?access_token={token}&page='
i = 1
while True:
a = get_json_from(url + str(i))
if not a:
break
data.extend(a)
i += 1
return data
i = 1
data = []
for piece in iter(partial(get_json_from, url+str(i)), False):
data.append(piece)
i += 1
有办法使它工作吗?
答案 0 :(得分:1)
您遗漏了一个重要点:iter()
带有一个 static 可调用对象,该对象的参数不能更改,但是重复调用f.read()
会返回不同的值。具有两个参数的iter()
函数将重复调用partial(f.read, 32)
(因此f.read(32)
),直到返回值与哨兵值匹配为止,这使得在循环中从文件读取有效地工作。>
您的get_json_from()
函数不会不执行此操作。重复调用具有相同参数的get_json_from()
不会改变返回值,因为get_json_from()
没有任何状态可以恢复。
您的参数不是动态的,传入url + str(i)
是因为该参数也不会从循环中取出i
,因为partial()
仅记录一次该值:
>>> from functools import partial
>>> i = 42
>>> p = partial(str, i + 10)
>>> p.args
(52,)
>>> i = 81
>>> p.args
(52,)
>>> p()
'52'
i + 10
表达式不是'live';结果仅计算一次,并以partial()
的形式传递给52
;在调用i
对象之前将81
设置为partial()
都没关系。
您可以使用一个可调用对象,该对象在每次调用时都会重新计算get_json_from()
的参数;一个lambda表达式可以做到这一点(将url
和i
作为父级作用域的闭包):
for part in iter(lambda: get_json_from(url + str(i)), None):
# ...
每次调用url + str(i)
对象时,都会计算lambda
。我假设当网址不存在时get_json_from()
返回None
,而不是False
。
但是,在您的情况下,您可以通过使用 generator函数将更改为i
值并将其绑定到可迭代状态,从而使代码更清晰:
def gen_data_from_user(user, type, token):
url = f'https://api.github.com/users/{user}/{type}?access_token={token}&page='
i = 1
while True:
a = get_json_from(url + str(i))
if not a:
break
yield a
i += 1
在生成器函数中,代码将暂停,直到您开始遍历调用函数返回的对象为止。进行迭代时,代码将一直运行,直到遇到下一个yield
表达式为止,这时功能代码再次停止,并为您提供表达式的值。
因此,在以上内容中,循环访问gen_data_from_user(....)
将为您提供a
循环中的每个while True:
。该函数将状态与局部变量保持在中间,因此i
会保留(以及url
),以在下次未暂停代码时使用。
然后您可以使用:
for piece in gen_data_from_user(...):
# ...
不需要iter()
,并且上面的内容比iter(lambda: ..., None)
的定义要干净得多。
答案 1 :(得分:1)
您可以使用lambda
而不是partial
来允许每次调用时重新评估内部变量:
for piece in iter(lambda: get_json_from(url+str(i)), False):