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
zhoudaiyu
V2EX  ›  Python

学了 1 年 Python ,今天看了段代码觉得白学了,求教一下大家

  •  
  •   zhoudaiyu · 2021-03-04 15:03:43 +08:00 · 4343 次点击
    这是一个创建于 1121 天前的主题,其中的信息可能已经有所发展或是发生改变。

    把代码精简了一下

    image.png

    我本以为输出结果会是个[[],[]],输出结果是个列表嵌套了两个列表,每个列表里又嵌套了 2 个列表...

    image.png

    猜测是个递归,debug 一下确实是这样的

    image.png

    有几个问题:

    1 、这个为啥是个递归?
    2 、这个递归为什么没有爆栈?
    3 、修改了一下代码,为什么返回了(True, True)?

    image.png

    第 1 条附言  ·  2021-03-05 10:41:07 +08:00
    感谢大家,基本理解了,我想问另外一个问题,就是这样写这个 root 列表会不会一直 gc 不掉?
    20 条回复    2021-03-06 01:14:51 +08:00
    chaosgoo
        1
    chaosgoo  
       2021-03-04 15:11:12 +08:00
    1.root 里面放了两个 root 的引用
    2.root 里面只是存了两个地址而已
    3.自己和自己比较当然是 True
    THESDZ
        2
    THESDZ  
       2021-03-04 15:14:03 +08:00
    实参,形参
    zhoudaiyu
        3
    zhoudaiyu  
    OP
       2021-03-04 15:15:27 +08:00
    @zhoudaiyu

    大家忽略最后一个问题,脑残了
    aijam
        4
    aijam  
       2021-03-04 15:16:16 +08:00
    相当于:
    root = []
    root.append(root)
    root.append(root)
    vicalloy
        5
    vicalloy  
       2021-03-04 15:17:22 +08:00
    同楼上,就是一个递推的引用,换成 dict 是不是就好理解多了。
    >>> o = {}
    >>> o['o'] = o
    >>> o
    {'o': {...}}
    Vegetable
        6
    Vegetable  
       2021-03-04 15:17:34 +08:00
    Circular reference detected

    给你化简一下吧
    root = []
    root.append(root)
    aijam
        7
    aijam  
       2021-03-04 15:17:49 +08:00
    先理解下面什么意思,也就好理解了
    root = []
    root.append(root)
    nznd
        8
    nznd  
       2021-03-04 15:22:27 +08:00
    >>> root=[]
    >>> id(root)
    2296081934592
    >>> id(root[:])
    2296081935040
    >>> root[:]=[root,root]
    >>> id(root)
    2296081934592
    >>> id(root[:])
    2296081935040
    >>> id(root[0])
    2296081934592
    >>> id(root[1])
    2296081934592
    简单来说 就是 root[0] 和 root[1] 都指向了 root,root[:]是 root 的一个指针(类似的概念 忘了叫啥了,刷 leetcode 可以用这个装作是原地算法(直接改这个值
    acmore
        9
    acmore  
       2021-03-04 15:33:35 +08:00
    魔法就是自指广义表:
    root = []
    root.append(root)

    另外基本上所有语言都可以这么玩,不只是 Python
    RockShake
        10
    RockShake  
       2021-03-04 15:35:41 +08:00
    自己引用了自己,死循环,print 无法打印出所有
    SystemLight
        11
    SystemLight  
       2021-03-04 15:45:25 +08:00
    root[:]=[1,2]和 root=[1,2]两种写法有啥区别
    karloku
        12
    karloku  
       2021-03-04 15:48:19 +08:00
    1.
    上面都回答了

    2.
    爆栈对应于堆栈. 变量引用不会建立堆栈, 自然没有爆栈问题.
    如果你准备用递归的函数来深层处理这个变量, 每次函数调用都会建立堆栈, 那基本就要爆了.
    karatsuba
        13
    karatsuba  
       2021-03-04 18:21:28 +08:00
    这个不限于[], 即 root=[1], root=root.append(root),也会是[1, [...]]
    Jirajine
        14
    Jirajine  
       2021-03-04 18:29:31 +08:00 via Android
    一切皆对象(的引用)
    因而所有变量都是引用类型(值类型可以看作是一种 inline 优化,因而可以看作引用类型)
    lizytalk
        15
    lizytalk  
       2021-03-04 18:47:49 +08:00
    你想说 root[0] == root 和 root[0] is root 都是 True ? root=root 和 root is root 是 True 没什么可说的吧
    dayeye2006199
        16
    dayeye2006199  
       2021-03-05 05:33:50 +08:00
    这个写法有什么实际用途吗?想学习一下魔法
    SjwNo1
        17
    SjwNo1  
       2021-03-05 09:29:51 +08:00
    可变类型 地址引用
    no1xsyzy
        18
    no1xsyzy  
       2021-03-05 10:19:45 +08:00
    数据自指不叫递归,因为它不会执行。

    @SystemLight 一个修改 list 内容,一个修改 locals() 绑定。
    cassidyhere
        19
    cassidyhere  
       2021-03-05 10:35:32 +08:00
    标准库 functools.lru_cache 把它当双向链表用
    摘部分代码:
    PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
    root = [] # root of the circular doubly linked list
    root[:] = [root, root, None, None] # initialize by pointing to self

    # Use the old root to store the new key and result.
    oldroot = root
    oldroot[KEY] = key
    oldroot[RESULT] = result
    # Empty the oldest link and make it the new root.
    # Keep a reference to the old key and old result to
    # prevent their ref counts from going to zero during the
    # update. That will prevent potentially arbitrary object
    # clean-up code (i.e. __del__) from running while we're
    # still adjusting the links.
    root = oldroot[NEXT]
    oldkey = root[KEY]
    oldresult = root[RESULT]
    root[KEY] = root[RESULT] = None
    lithbitren
        20
    lithbitren  
       2021-03-06 01:14:51 +08:00
    只要引用名归零或者作用域销毁,循环引用也是可以被 GC 的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2889 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 14:20 · PVG 22:20 · LAX 07:20 · JFK 10:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.