Python里的全局变量、局部变量、类的self.*

文章讨论了在LeetCode第538题中,两种不同的解决方案——使用全局变量和类属性的区别。作者通过实例解释了全局变量在函数内部的作用,以及为何在函数内部修改全局变量可以影响到外部调用。关键点在于理解Python中的值类型和引用类型,以及全局变量的正确使用方式。
摘要由CSDN通过智能技术生成

问题

困惑,leetcode第538题,写成下面这种,不能得到正确的答案:

class Solution_a:
    def traverse(self, root, cur_sum):
        # 思路:中序遍历可以打印出有序的数组,那么反一下,先右后中再左就可以打印出降序的数组,然后再维护一个字段记录累计和
        if not root:
            return

        self.traverse(root.right, cur_sum)
        cur_sum += root.val
        root.val = cur_sum
        self.traverse(root.left, cur_sum)

        return

    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
    	global cur_sum
    	cur_sum = 0
        self.traverse(root, cur_sum)

        return root

但是改成下面这样(将cur_sum初始化为一个属性),就可以了:

class Solution_a:
    def __init__(self):
        self.cur_sum = 0

    def traverse(self, root):
        # 思路:中序遍历可以打印出有序的数组,那么反一下,先右后中再左就可以打印出降序的数组,然后再维护一个字段记录累计和
        if not root:
            return

        self.traverse(root.right)
        self.cur_sum += root.val
        root.val = self.cur_sum
        self.traverse(root.left)

        return

    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        self.traverse(root)

        return root

思考

本质的区别和原因是什么呢?

第一种写法下,我以为是:我在convertBST函数里定义了一个全局变量cur_sum(后面会醒悟这个以为也是错误的,因为全局变量不会在一个函数内部去定义),但是调用traverse时,cur_sum是以一个参数的形式传入的,当函数内部存在一个与全局变量同名的局部变量时,会使用自己内部的局部变量。举个二叉树的例子,用第一种写法调用的结果是:
在这里插入图片描述

如果这个以为是对的话,那我不把cur_sum以参数的形式传入应该就行了,试试看:
在这里插入图片描述
报错如下,这说明全局变量就不是这么定义的,不然traverse内部怎么找不到它?在这里插入图片描述

不死心,我尝试再改一下这个定义的位置看看:
在这里插入图片描述

依然不行!!还不死心,我再改一下位置,直接放在调用的地方试试:
在这里插入图片描述

依然不对!!!说明global 变量名 并不是我以为的用来定义一个全新的全局变量,然后就到处可以用了!global到底怎么用,问问GPT的到如下回复:

在Python中,global关键字用于在函数内部声明一个全局变量,以便在函数内部对全局变量进行修改。

好的,小菜鸡认为自己悟了,再给我一次机会,修改成下图,老老实实的在所有函数外部定义一个全局变量,在traverse中想要更改全局变量时,使用global关键字(如果没有将其声明为全局变量,则在函数内部更改或创建的任何变量都是局部变量),规范操作:
在这里插入图片描述

!!!啊啊啊,它终于对了!但是刷leetcode的时候不方便这么写啊,因为调用的代码在外面,不是咱自己写的呢。所以还是老老实实用第二种方法吧。

第二种写法self.cur_sum相当于在一开始,就初始化了一个属性,在类的所有函数里都可以调用(很像全局变量的感觉),所以可以得到正确结果。
在这里插入图片描述

总结

  1. 全局变量的定义:没有在任何函数内部定义,并且具有全局范围的变量;
  2. 局部变量的定义:在函数内部定义的变量,范围仅限于该函数;
  3. python中global关键字的用法:是在一个函数内部声明一个全局变量,以便在函数内部对全局变量进行修改,这个全局变量本身已经定义过了,而不是我想当然认为的定义了一个全新的全局变量!‼️如果没有将其声明为全局变量,则在函数内部更改或创建的任何变量都是局部变量。
  4. 当全局变量和局部变量的命名一样时,在一个函数内部还是会使用自己内部的局部变量;

问题延伸

那怎么理解下面这段代码里root的更新呢?root以一个参数的形式传递给了traverse,在traverse的内部修改了节点的value,这些修改的操作最终在convertBST函数里返回,为什么也可以生效?(原地修改的原理)

因为root是可变对象,具体见下图。在函数内部修改树的节点值时,即使没有显式的返回root,原始的root也会被更新。
在这里插入图片描述

class Solution_a:
    def __init__(self):
        self.cur_sum = 0

    def traverse(self, root):
        # 思路:中序遍历可以打印出有序的数组,那么反一下,先右后中再左就可以打印出降序的数组,然后再维护一个字段记录累计和
        if not root:
            return

        self.traverse(root.right)
        self.cur_sum += root.val
        root.val = self.cur_sum
        self.traverse(root.left)

        return

    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        self.traverse(root)

        return root

参考资料

  1. Python全局变量和局部变量
  2. Python中的值类型与引用类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值