V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
plko345
V2EX  ›  Python

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

  •  
  •   plko345 · 41 天前 · 1067 次点击
    这是一个创建于 41 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下, 为什么 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
    
    11 条回复    2021-09-16 20:03:17 +08:00
    Divinook
        1
    Divinook   41 天前
    因为你的递归没有终止条件,改成
    return next(filter_a(k,v), {'a', 2})
    plko345
        2
    plko345   41 天前
    @Divinook 可是我应该从 list 里取到 {'a': 2, 'b':'冬天'}
    hsfzxjy
        3
    hsfzxjy   41 天前 via Android
    参数会先于函数调用求值,所以不管 iter 是不是空 return next(filter_a(k,v), get_a('a',2)) 里的 get_a('a', 2) 都会执行
    plko345
        4
    plko345   41 天前
    @Divinook 而且 filter 返回是非空的, next 应该直接返回该值才对, 但却不断的返回 default
    plko345
        5
    plko345   41 天前
    @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
        6
    plko345   41 天前
    @hsfzxjy

    In [15] : next(filter(lambda x: x==3, [0,1,2]), get_b('abc'))
    Out[15]: 'a'
    princelai
        7
    princelai   41 天前
    def get_a(k,v):
    return next(filter_a(k,v), next(filter_a('a',2)))

    这样呢
    2i2Re2PLMaDnghL
        8
    2i2Re2PLMaDnghL   41 天前   ❤️ 1
    你在 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
        9
    2i2Re2PLMaDnghL   41 天前
    #4 不是返回 default,而是先求 default 再求 next
    但因为求 default 导致了无限递归,你永远不会求 next
    不妨试试这样写
    original_next = next
    def next(*args):
    ... print("running next()")
    ... return original_next(*args)
    你再试试,你会发现 "running next()" 一次也没被打印。
    plko345
        10
    plko345   41 天前
    @2i2Re2PLMaDnghL 我上个月才看过 SICP 这部分, 完全没印象了......接受处罚

    我试过打印, 确实是的
    plko345
        11
    plko345   41 天前
    我还是乖乖返回 None 再做次判断吧
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3843 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 07:29 · PVG 15:29 · LAX 00:29 · JFK 03:29
    ♥ Do have faith in what you're doing.