浅谈Python3函数命名空间与作用域

日期:2020年1月23日
作者:Commas
注释:前一章节讲述了命名空间和作用域的知识,现在我们来谈一谈Python3函数的命名空间吧。
如果您想了解更多有关Python的知识,那么请点《我的Python浅谈系列目录》


一、函数名的本质

函数名的本质是一个存储函数体本身的十六进制地址的变量,也可以说是一个指向函数体本身的指针,如下所示:

def hello():
    print("hello")

print(hello)

# 控制台输出:
<function hello at 0xcc485270>

既然函数名本质上是一个变量,那么就可以赋值给其它变量,如下所示:

def hello():
    print("hello")

a = hello
print(a)

# 控制台输出:
<function hello at 0xcc485270>

函数的调用,就是在函数名后面加一对英文圆括号,即函数名(),亦可理解为函数地址(),如下所示:

def hello():
    print("hello")

a = hello
print(a)
# 通过变量a调用函数hello()
a()

# 控制台输出:
<function hello at 0xcc485270>
hello

二、函数命名空间

函数命名空间属于局部命名空间,即函数中可以直接访问内置命名空间和全局命名空间的标识符(变量和非内嵌函数),但是内置命名空间或全局命名空间中不可访问函数内的标识符(变量和内嵌函数)。
比方说一个函数相当于一个车次的列车,接下来我们感受一下奇妙之旅吧:

  1. 在同一车次内,是不可以有重复的命名,每个车厢号命名都不一样,倘若一样,那我们无法区分该上哪个车厢,一切将会乱套。变量也一样,即在同一个命名空间中,变量命名不可重复。
  2. 在不同车次,变量名是可以重复的。1次列车有1至22节车厢,每节车厢分别为1、2、……、22;2次列车可以这样,3次列车同样也可以这样命名,它们之间是互不影响的,比方说我的座位是1次列车的11号车厢,可以很明确表达一个信息,没有二义性。变量也一样,即在不同命名空间中,变量命名是可以重复的。
  3. 在同一车次,每节车厢相当于一个内嵌函数,假设每节车厢里面都有一个名字为张三的人,车站上也有一个张三。我在11号车厢叫张三,你车票丢了,11号车厢的张三会跑过来认领,车站上的张三则不会。也就是说,遵循一个就近原则的规则。变量也一样,即在本命名空间有,那么你要访问的就是本命名空间的;本命名空间没有,那么就往上一层命名空间找,直到找到位置,倘若找到内置命名空间还是没有,那么就抛出异常。(这下知道为什么异常是在最外层抛出的了,当然远比这复杂)
  4. 在列车外,也就是不在车上,那么我也不可以使用1车次1号车厢21号座位的票,但是我可以选择上车,就相当于我调用了“上车”这么一个函数变量也一样,即在本命名空间外层的所有命名空间,函数内的变量是不能被访问的,但是本函数还是可以被调用或者函数名(变量)被赋值给某个变量

三、函数作用域

说起作用域,得配合着函数的命名空间一起讲。作用域,从字面上理解就是作用的区域,一个作用的范围,在这个范围内,是生效的,范围外,就不生效了。
函数的命名空间属于局部命名空间,函数中标识的作用域也就是在这个局部命名空间中起作用。

  1. 【对上】:
    对局部命名空间外,即全局命名空间中的变量,常规下修改是不生效的,当然可以采用特殊手段——使用关键字global定义局部变量(此法不推荐),将局部命名空间提升至全局命名空间,这么一来,作用域也就被提升为全局,将会影响全局的变量值,如下所示:
a = 1

def modify_a():
    # 使用global关键字定义a,将局域变量a提升至全局变量,作用于全局,在全局将会输出2
    # 如果不使用global,那么a输出的值仍为1
    global a
    a = 2

modify_a()
print(a)

# 控制台输出:
2
  1. 【对下】:
    对局部命名空间以下的“子孙命名空间”,即嵌套命名空间的变量是不生效的,而“子孙命名空间”因为保存了“父级命名空间”的引用,形成了一条关系链,所以可以访问自己所有的“父级命名空间”,但是不可修改其变量,当然可以采用特殊手段——使用关键字nonlocal定义变量(此法不推荐),将变量命名空间逐级向上寻找第一个有相同变量名的命名空间(就近原则再次被使用啦),这么一来,作用域也就被提升,将会影响被寻到的命名空间的变量值。值得一提的事,如果找到最外层局部命名空间还没有此变量,那么就抛出异常。如下所示:
# ①抛出异常版本
a = 1

def outer():
    outer_a = 111
    # 定义内嵌函数inner()
    def inner():
        nonlocal a
        a = 222
    # 调用内嵌函数inner()
    inner()
    print(a)

# 调用函数outer()
outer()
print(a)

# 控制台输出:
    nonlocal a
    ^
SyntaxError: no binding for nonlocal 'a' found
# ②正确使用nonlocal版本
a = 1

def outer():
    outer_a = 111
    # 定义内嵌函数inner()
    def inner():
        nonlocal outer_a
        outer_a = 222
    # 调用内嵌函数inner()
    inner()
    print(outer_a)

# 调用函数outer()
outer()
print(a)

# 控制台输出:
222
1
  1. 【对本身】
    除了“对上对下”外,就是本身,本身的变量名是不可以重复的,会互相影响,大家都比较清楚,这个我就不过多赘述了。

版权声明:本文为博主原创文章,如需转载,请给出:
原文链接:https://blog.csdn.net/qq_35844043/article/details/104074353

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Commas.KM

码路共同进步,感恩一路有您

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值