python3中 for 循环中循环变量的作用域问题

python3中 for 循环中循环变量的作用域问题

前段时间 对实际项目重构代码,然后在我给变量重新命名的时候,
for 循环中循环变量名 和外层 的变量名,取名相同,结果直接覆盖了外层变量的值, 由此 我就查了一下 文档有了这篇文章。

1-1 来看一个例子

编写 hello.py


for num in [1, 5, 10]:
    print(num,end=' ')

print(f"=== after for:{num}")

结果输出
1 5 10 === after for:10

num 这个变量 在 for 循环代码块 结束后,仍然有效。

也就是说在 for 循环 迭代 一个可迭代对象的时候,离开for 循环的代码段,这个 循环变量num 依然有效 。

1-2 来看第二个例子

如果迭代是 对象没有值的话,num 就不会赋值 这个时候,for 循环 就不能进入,此时num 也没有赋值,即没有num 这个变量.


for num in []:
    print(num,end=' ')

print(f"=== after for:{num}")

结果如下:

img-01

1-3 循环变量 覆盖 外层变量的问题

如果我在 模块级别定义一个num 的变量,然后 for 循环的索引变量 也是num 并且 迭代对象 不为空的时候, 这个时候 就有可能出现 问题,

因为此时 for 循环的 索引变量 num 会覆盖外层的变量, 使外层变量的值发生改变。


num =100

for num in [1,2,3]:
    print(num,end=' ')

print(f"=== after for:{num}")

结果如下:
1 2 3 === after for:3

从结果可以看出 num 的值是3 ,说明 经过 for 循环后 ,num 最后赋值为3, 和 上面 num =100 有冲突,直接 把 num 的值 覆盖了。
这是因为 外层num 和 for 循环里面的 num 是处于一个作用域, 都是模块级别的变量。 所以都在模块级别的变量,不能重复。

那么如何避免这种问题呢?

方法一:
修改 循环变量的名称,不要和外层变量名一样,这样两个变量名称,就不会冲突了。

方法二 :
可以把for 循环里面的内容 封装成一个函数,这样就可以 避免变量名 覆盖的问题。

# -*- coding: utf-8 -*- 

num = 100


def print_num():
    for num in [1, 2, 3]:
        print(num, end=' ')
    print()


if __name__ == '__main__':
    print_num()
    print(f"=== after print_num:{num}")
    pass

结果如下:
img-02

从结果可以看出, num 没有被污染, 这两个 num 在不同的作用域里面。
这样python 解释器 就不会覆盖。

2 python 变量的作用域的问题

正好来一起来探讨 变量作用域的问题 ,
作用域 就是指 一个变量的作用范围在哪里。 一个变量的作用范围 或者说 在哪一代码段 是有效的。

在python中

全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

全局名称 就是 一般定义在模块级别的变量。

局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

局部 是指 定义在函数 或者类中的一些变量。

2-1 来看一个 全局变量的例子

# -*- coding: utf-8 -*-

# 全局变量
num = 100



print(f"module num:{num}")


def visit_num():
    print(f"visit num:{num}")





if __name__ == '__main__':
    visit_num()
    print(f"main num:{num}")
    pass

由于num 定义在 模块级别 是一个全局变量,此时 num 之后的所有代码 都可以访问到这个变量的。 这就是全局变量

2-2 如何修改一个全局变量呢

你可能 直接感觉 visit_num 函数里面修改这个变量不就可以了吗,

如下面的代码段

# -*- coding: utf-8 -*-


num = 10000000

print(f"module num:{num}")


def visit_num():
    print(f"visit num:{num}")
    num = 0


if __name__ == '__main__':
    visit_num()
    print(f"main num:{num}")

运行代码发现会报错

img-05

原因是 解释器无法知道 num=0 是想在 visit_num() 这个函数定义一个变量叫num,还是想修改 上面定义的全局变量,所以 就报错了。

那么如何让 解释理解 num 是要修改 全局变量呢?

# -*- coding: utf-8 -*-

num = 10000000

print(f"module num:{num}")


def visit_num():
    # 这里使用关键字进行声明
    global num
    print(f"visit num:{num}")
    # modify num
    num = 0


if __name__ == '__main__':
    visit_num()
    print(f"main num:{num}")

这样就可以正常 的修改 num 的值了。 就是需要在 函数前面的位置 用 global 关键字 进行声明 num, 这样解释器 就能明白我现在要 更改的是一个全局变量里面的num.

2-3 在函数中定义自己的变量

思考一下:
如果想没有添加 global 声明 ,然后直接修改num 的值 ,会有什么结果呢?

# -*- coding: utf-8 -*-

num = 10000000
print(f"module num:{num}")


def visit_num():
    # modify num
    num = 0
    print("modify  num done.")


if __name__ == '__main__':
    visit_num()
    print(f"main num:{num}")
    pass

结果如下:
img-06

可以看出 这里并没有把num 的值 修改,原因是 在visit_num 中定义了一个新的 num 变量,这个变量 和外层的变量 不在一个scope 里面, 一个是局部变量,一个是全局变量。 所以 python解释器 就可以知道这是新定义的一个变量。 和外层的模块级别的 num 变量没有关系。

所以 简单总结一下, 如果在函数中定义了一个和模块级别的变量的相同的名称,这两个变量 是 不相互影响的。

但是如果都在模块级别定义相同的变量名称,就会出现变量名 被覆盖的情况。 不论这个变量出现在 for 循环,还是 其他的任何位置,只要 这段代码被执行了,就会覆盖之前的变量。


num = 1000

for num in [1, 2]:
    pass

但是有一种比较特殊的情况,对于列表推导式的情况下,它的循环变量只在 自己的列表推导式里面, 出了列表推导式,变量就不存在了。


>>> l = [num ** 2 for num in range(5)]
>>> l
[0, 1, 4, 9, 16]
>>> num
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'num' is not defined

对于推导式的作用域就类似 在函数里面了, 出了列表推导式,变量 就失效了。

总结

平常还是要多留意不同语言之间的差别,每一种语言的设计,都有每一种语言的设计理念,只有理解的作者的理念,才能体会到语言之美,不断总结吧,也给自己一个小小的教训,不要想当然的认为 ,这个应该是这样的,多去实践,来去检查自己的想法。

分享快乐,留住感动. '2020-10-24 09:54:18' --frank
  • 29
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值