遇到一个最大递归深度的错误, 无法理解, 请大家帮忙看看

2021-09-16 17:12:13 +08:00
 plko345

代码如下, 为什么 get_a 没有返回默认?

In [7]: a = [{'a':1},{'a':2}]

In [8]: a
Out[8]: [{'a': 1, 'b':'夏天'}, {'a': 2, 'b':'冬天'}]

In [9]: def filter_a(k,v):
   ...:     return filter(lambda x: x[k] == v, a)
   ...: 

In [10]: def get_a(k,v):
    ...:     return next(filter_a(k,v), get_a('a',2))    # 我的想法是如果 iter 为空, 返回 这个默认
    ...: 

In [11]: get_a('a',1)

报错如下

---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-11-428f62971dec> in <module>
----> 1 get_a('a',1)

<ipython-input-10-a36c60ef3685> in get_a(k, v)
      1 def get_a(k,v):
----> 2     return next(filter_a(k,v), get_a('a',2))
      3 

... last 1 frames repeated, from the frame below ...

<ipython-input-10-a36c60ef3685> in get_a(k, v)
      1 def get_a(k,v):
----> 2     return next(filter_a(k,v), get_a('a',2))
      3 

RecursionError: maximum recursion depth exceeded while calling a Python object
1820 次点击
所在节点    Python
11 条回复
Divinook
2021-09-16 17:35:22 +08:00
因为你的递归没有终止条件,改成
return next(filter_a(k,v), {'a', 2})
plko345
2021-09-16 17:53:42 +08:00
@Divinook 可是我应该从 list 里取到 {'a': 2, 'b':'冬天'}
hsfzxjy
2021-09-16 17:54:17 +08:00
参数会先于函数调用求值,所以不管 iter 是不是空 return next(filter_a(k,v), get_a('a',2)) 里的 get_a('a', 2) 都会执行
plko345
2021-09-16 17:55:29 +08:00
@Divinook 而且 filter 返回是非空的, next 应该直接返回该值才对, 但却不断的返回 default
plko345
2021-09-16 17:59:49 +08:00
@hsfzxjy 可是像这样却没有问题

```py
In [13]: def get_b(x):
...: return x[0]
...:

In [14]: next(filter(lambda x: x==0, [0,1,2]), get_b('abc'))
Out[14]: 0
```
plko345
2021-09-16 18:00:57 +08:00
@hsfzxjy

In [15] : next(filter(lambda x: x==3, [0,1,2]), get_b('abc'))
Out[15]: 'a'
princelai
2021-09-16 18:13:28 +08:00
def get_a(k,v):
return next(filter_a(k,v), next(filter_a('a',2)))

这样呢
2i2Re2PLMaDnghL
2021-09-16 19:47:11 +08:00
你在 get_a 里面还没进迭代器呢就先重复 get_a 了,罚您重看 SICP 1.1.5
next 的参数不会懒惰求值,咱把 get_a 写成 applicative order
def get_a(k,v):
t1 = filter_a(k,v)
t2 = get_a('a',2)
t3 = next(t1, t2)
return t3
显然,计算 t2 的时候就已经无限递归了

你需要的大概是
try: return next(filter_a(k,v))
except StopIteration: return get('a',2)
或者 chain 一个生成器上去
next(itertools.chain(filter_a(k,v), (get_a('a',2) for _ in range(1))))

但也有个问题,如果没有 x['a']==2 的记录,还是会无限递归。
2i2Re2PLMaDnghL
2021-09-16 19:51:10 +08:00
#4 不是返回 default,而是先求 default 再求 next
但因为求 default 导致了无限递归,你永远不会求 next
不妨试试这样写
original_next = next
def next(*args):
... print("running next()")
... return original_next(*args)
你再试试,你会发现 "running next()" 一次也没被打印。
plko345
2021-09-16 20:01:20 +08:00
@2i2Re2PLMaDnghL 我上个月才看过 SICP 这部分, 完全没印象了......接受处罚

我试过打印, 确实是的
plko345
2021-09-16 20:03:17 +08:00
我还是乖乖返回 None 再做次判断吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/802340

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX