求教 Python 问题, GPT o1 跟 Gemini 2.0 都解决不了 - V2EX
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
superhot
V2EX    Python

求教 Python 问题, GPT o1 跟 Gemini 2.0 都解决不了

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

    首先我理解这样的写法并非最佳实践,只是想理解语言差异与背后的原因。

    这是一道 LeetCode 题,讨论区里面每种语言都用了相同的解法,但只有 Python 以同样的写法陷入了死循环,问了 AI 都说两种写法等价,所以不应该存在 bug ,但实测显然是不同的,所以想问问原因。

    注意看 while 循环里为两个指针连续赋值的地方。

    C++ 版

    ListNode *partition(ListNode *head, int x) { ListNode node1(0), node2(0); ListNode *p1 = &node1, *p2 = &node2; while (head) { if (head->val < x) p1 = p1->next = head; else p2 = p2->next = head; head = head->next; } p2->next = NULL; p1->next = node2.next; return node1.next; } 

    Java 版

    public ListNode partition(ListNode head, int x) { ListNode smallerHead = new ListNode(0), biggerHead = new ListNode(0); ListNode smaller = smallerHead, bigger = biggerHead; while (head != null) { if (head.val < x) { smaller = smaller.next = head; } else { bigger = bigger.next = head; } head = head.next; } // no need for extra check because of fake heads smaller.next = biggerHead.next; // join bigger after smaller bigger.next = null; // cut off anything after bigger return smallerHead.next; } 

    我自己翻译的 Python 版,然而死循环导致超时:

    class Solution: def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: lt, ge = ListNode(), ListNode() ltPtr, gePtr = lt, ge while head: if head.val < x: ltPtr = ltPtr.next = head else: gePtr = gePtr.next = head head = head.next ltPtr.next, gePtr.next = ge.next, None return lt.next 

    翻了一下讨论区,下面的是没问题的 Python 版

    class Solution: def partition(self, head: ListNode, x: int) -> ListNode: head1 = tail1 = ListNode() head2 = tail2 = ListNode() while head: if head.val < x: tail1.next = tail1 = head else: tail2.next = tail2 = head head = head.next tail2.next = None tail1.next = head2.next return head1.next 

    问题就出在了为两个指针连续赋值的地方,所以为什么在 Python 中,p = p.next = qp.next = p = q 是不等价的呢?分别都是怎样的步骤进行赋值的?

    15 条回复    2024-12-27 19:48:26 +08:00
    gydi
        1
    gydi  
       336 天前
    简单用 python 测试了一下,有下面这样的结果。
    >>> b = {'next': 'b'}
    >>> c = {'next': 'c'}
    >>> b = b['next'] = c
    >>> print(b,c)
    {'next': {...}} {'next': {...}}
    >>> b = {'next': 'b'}
    >>> c = {'next': 'c'}
    >&t;> b['next'] = b = c
    >>> print(b,c)
    {'next': 'c'} {'next': 'c'}

    于是猜测 python 的执行对于 a=b=c 来说应该是 a=c;b=c 这样。然后搜了一下,发现这个文章 https://chowyi.com/key-points-of-chained-assignment-in-python/
    rrfeng
        2
    rrfeng  
       336 天前 via Android
    我觉得是 c 的遗留

    a = ( b = c )

    ( b = c )这个赋值表达式是有值的,等于 c
    wang93wei
        3
    wang93wei  
       336 天前
    zhouyin
        4
    zhouyin  
       336 天前
    @rrfeng
    java 和 c++也是 c 系
    照理说 a=b=c a 也等于 c 啊 不是吗?
    wang93wei
        5
    wang93wei  
       336 天前
    我之前写 SQL 的时候也犯过同样的问题。在 where 条件内弄了一个三个值的等式,取决于语言特性,导致 where 条件并不成立。
    cmdOptionKana
        6
    cmdOptionKana  
       336 天前   3
    建议不要去搞懂 a=b=c ,同时不管哪种语言都不要使用 a=b=c ,一律拆开来写。
    superhot
        7
    superhot  
    OP
       336 天前 via Android
    @wang93wei 这个回答非常清晰…看来我跟 AI 对话时的描述与问题不够好
    superhot
        8
    superhot  
    OP
       336 天前 via Android
    @cmdOptionKana 我知道这种写法不好 只是邯郸学步 不小心踩了坑 想搞明白为啥摔了 真正工程中可不敢这么写
    aijam
        9
    aijam  
       336 天前
    a=b=c 在 python 里是 leftmost 所以相当于
    aijam
        10
    aijam  
       336 天前   1
    a=b=c 在 python 里是 leftmost 所以相当于
    a=c
    b=c

    但在其他 c 系语言里相当于
    b=c
    a=b
    aijam
        11
    aijam  
       336 天前
    @aijam
    所以其他的语言里 p1 = p1->next = head;
    p1->next 先被指向了 head (此时 p1 还不是 head ),p1 才被指向 head
    但在 Python 里,ltPtr = ltPtr.next = head
    ltPtr 先被指向了 head, ltPtr.next 才被指向 head ,此时 head.next 就被指向了 head 本身,是一个循环链表
    cmdOptionKana
        12
    cmdOptionKana  
       336 天前
    @superhot 懂了就可能忍不住用,还不如不懂,更容易杜绝使用。而且这个也没啥技术因素,纯粹人为规定,属于“偏好”,偏好是不值得学的。
    rrfeng
        13
    rrfeng  
       336 天前
    触发个报错更明显了:

    ```
    >>> b={'n':0}
    >>>
    >>> b = b['n'] = 1
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: 'int' object does not support item assignment
    ```
    NoOneNoBody
        14
    NoOneNoBody  
       336 天前   1
    python 表达式计算顺序从左至右,且参看手册 Lexical analysis 最后部分
    https://docs.python.org/3/reference/lexical_analysis.html#delimiters
    = 属于分隔符,而多个分隔符两侧优先级相同,则从左至右
    最左边变量先得到值,中间变量后得到值


    假设 p,q 都含有 next 属性,p.next 为整数,q.next 为 None
    p.next = p = q
    p 后赋值,p.next 最终为 None ,但它是两次变化:先是 q 对象,p 赋值为 q 对象时,p.next 也变为 q.next 的值
    实际上只写 p=q 就够了,先执行的 p.next=q 被后面 p“覆盖”了

    p = p.next = q
    p.next 后赋值,p.next 为 q 对象,p.next.next 才是 None
    这个过程 p.next 两次变化:第一次为 None ,第二次是 q 对象
    因为两次变化,实际上最终 p!=q

    *** 最主要原因是 python 大部分赋值为“引用”
    懂的话也不用解释,不懂的话,很难一两句说清,请查阅手册和相关文章
    但可以“傻瓜式”理解,就是除了 int/bool, float, complex, str, bytes 这些单值以外,复合的类型全部都要考虑引用问题
    最简单例子( b 引用 a ):
    a=[1,2,3]
    b=a
    c=[1,2,3]
    a[1]=5
    print(b,c) # 显示为[1,5,3], [1,2,3]

    所以,如果上述 q 为单值的话(应该说没有 next 属性),p=p.next=q 会直接报错,此题是因为所赋值恰好也有 next 属性,所以没有报错而已
    franklinyu
        15
    franklinyu  
       336 天前 via iPhone
    正因些奇奇怪怪的歧,一些新言直接不支持式值,我得挺好
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2608 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 04:14 PVG 12:14 LAX 20:14 JFK 23:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86