怎么理解Python变量作用域

发布时间:2021-11-11 14:59:46 作者:iii
来源:亿速云 阅读:131

这篇文章主要介绍“怎么理解Python变量作用域”,在日常操作中,相信很多人在怎么理解Python变量作用域问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么理解Python变量作用域”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

闭包就是, 函数内部嵌套函数. 而 装饰器只是闭包的特殊场景而已, 特殊在如果外函数的参数是指向一个, 用来被装饰的函数地址时(不一定是地址哈, 随意就好) , 就有了 "@xxx" 这样的写法, 还是蛮有意思的. 装饰器的作用是 在不改变原函数的代码前提下, 额外给原函数填写新功能. 写法上来看, 还是比较简洁优雅的.

装饰器的通俗写法

# 装饰器的通用写法def out(func):  def inner(*args, **kwargs):    print("we are checking...", args[0])    return func(*args, **kwargs)  return inner@outdef check_2019_nCov(name):  return f"now, {name} is very healthy..."tmp = check_2019_nCov('youge')print(tmp)# outputwe are checking... yougenow, youge is very healthy...

给装饰器传参

虽然这种 "@" 的写法, 是要求 外函数的参数是一个 func 地址 , 但要达到可以传参, 只要 再在外面包一层函数 (作用是接受参数) , 这样不就相当于扩大作用空间, 拿到参数了呀 .

# 最外层的函数作用是, 给装饰器传递参数def get_param(*args, **kwargs):  def out(func):    def inner(*args, **kwargs):      print("get params", args, kwargs)      return func(*args, **kwargs)    return inner  return out@get_param("youge")def check_2019_nCov(name):  return f"now, {name} is very healthy..."tmp = check_2019_nCov("youge")print(tmp)# outputget params ('youge',) {}now, youge is very healthy...

这种个装饰器传递参数的应用场景, 在 Web应用中, 以 Flask 为例, 就是所有的 路由 url 的概念呀, 如 route("/login") 这样的写法, 其原理就是用各种装饰器来实现 路由 -> 视图 的映射关系的.

仔细一看, 整个过程忽略了一个重要的话题, 即命名空间, 及 变量的作用域, 或者说命名空间如怎样的.

LEGB 法则

命名空间

前篇已经详细阐述过了, Python 变量的本质是指针, 是对象的引用, 而 Python中 万物皆对象. 这个对象是真正存储数据的内存地址, 是各种类(数据类型, 数据结构) 的实例. (变量就是用来引用对象的) 差不多这个意思吧.

最为直观的解释:

" A namespace is a mapping from names to objects". (变量名和对象的映射)

"Most namespaces are currently implemented as Python dictionaries." (大部分命名空间通过字典来实现)

即命名空间是用来 避免变量命名冲突 的约束. 各个命名空间是彼此独立的, 一个空间中不能重名, 不同空间中是不没有关系的. 就跟 计算机系统, 存储文件是样的逻辑.

for i in range(10):  print(i)# 这两句话都用到了 i 但其各自的空间是不一样的.[i for i in range(100)]

内置空间: (built-in names): Python 内置名称, 如内置函数,异常类...全局空间: (global names): 常量, 模块中定义的名称(类, 导入模块)...Enclosed: 可能嵌套在函数内的函数等...局部名称: (local names): 函数中定义的名称(函数内的变量) ...

Python 查找变量顺序为:Local -> Enclosed -> Global -> Built-in。

其实, 从我个人经验而言, 能区分 局部和全局 的 相对性. 就好了, 基本上. 直观上, 以一个写代码的 py文件为例. 最外层有, 变量, 类定义, 函数定义, 从from .. import .. 的变量或函数名, 这些就是 全局变量, 最外面的类或者函数, 里面是各自的名字空间呀.

# var1 是 globalvar1 = 666def foo():  # var2 是局部  var2 = 666  def foo2():    # 内嵌的局部    var3 = 666    # print(var2)print(var3) # G->L 是找不到的哦# 在 foo2 中 寻找 var2 是 L->E 是ok的# 在 foo 中 寻找 var2 是 E->L 是不行的

其实很好理解的. 就上段code来说,根据 L-E-G-B 法则, 其实理解一个 相对 就可以了.

全局 vs 局部

total = 0 # 全局def sum(a, b):  """重写内置sum"""  total = a + b  print("局部total:", total)sum(1, 1)print("全局total:", total)# output局部total: 2全局total: 0

可以看到, 局部是不会改变全局的哦, 而在局部内是可以拿到全局变量的. 不然闭包, 外函数接收的参数, 内函数怎么可以拿到呢? 就是外函数, "扩充了" 内函数的作用域呀, 根据 L->E->G->B 法则去搜索到.

global 和 nonlocal

name = "youge"def change_name():  name = "youyou"# 希望将 "youge" 改为 "youyou"change_name()print(name)# outputyouge

发现没有能改掉, 这是自然的. 因为, 在调用函数时, 里面的 name 是一个 Local 变量, 是不会影响到全局的 name的, 如果想实现在 在函数内部来改变 全局变量, 则将 该变量用 global 关键字声明即可.

name = "youge"def change_name():  global name  name = "youyou"# 希望将 "youge" 改为 "youyou"change_name()print(name)# outputyouyou

很简单, 在函数内部, 用 global 将其声明为全局变量即可. 同样, 针对于** 函数嵌套, 即向闭包, 装饰器等, 通过 关键字 nonlocal 实现将 函数内的变量, 声明为 函数外的 Enclose 层**

name = "jack"def outer():  name = "youge"  # 函数内有一个local函数空间  def inner():    name = "youyou"    print("local:", name)  inner() # 尝试改掉 嵌套层的 name  print("encolse:", name)print("global:", name)outer()# outputglobal: jacklocal: youyouencolse: youge

现在想在, inner函数 (L层) 中来修改 E 层的 name, 即在inner中将 name 声明为 nonlocal 即可.

name = "jack"def outer():  name = "youge"  # 函数内有一个local函数空间  def inner():    nonlocal name    name = "youyou"    print("local:", name)  inner() # 尝试改掉 嵌套层的 name  print("encolse:", name)print("global:", name)outer()# outputglobal: jacklocal: youyouencolse: youyou

函数嵌套场景中, 通过 在 local 层, 声明 name 为 nonlocal 则将 enclosed 层的name改掉了. 但如果在 local 层 声明 global 则是没有其效果的, 为啥, 嗯... 暂时还不清楚, 也是实验的, 暂时.

哦, 突然想贴一个, 我还是菜鸟时常, 犯的小错误:

name = 'youge'def change_name():  name = name + "youyou"change_name()print(name)# outputUnboundLocalError: local variable 'name' referenced before assignment

原因就在于, 在函数内部的空间中, 对 name 是没有定义的. 在 Python中, 对于函数过程的存储, 是通过 递归栈 实现的. 利用栈的 FILO, (先进后出) 的特点, 当遇到一个函数, 就用栈将其参与的成员, 依次入栈, 如有 return 则将置为栈元素.

变量要先定义, 后使用嘛, Python中的定义是指, 该变量指向某个实例对象即可, 而非 其它语言中的 类型声明 哦, 这里最容易混淆.

修改 name 为全局变量,通过函数参数传递即可:

# 方式1: 定义个单独的函数来处理name = 'youge'def change_name(s):  name = s + "youyou"  print(name)# 全局变量来传递给 函数空间, 即"先定义, 后执行")change_name(name)  # outputyougeyouyou

# 方式2: 声明为全局即可, 不推荐name = 'youge'def change_name():  global name  name = name + "youyou"change_name()print(name)# outputyougeyouyou

到此,关于“怎么理解Python变量作用域”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. python学习笔记4:变量作用域
  2. Python变量作用域是什么?怎么用?

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

python

上一篇:AndroidStudio项目适配AndroidX(Android9.0)的方法教程

下一篇:Django中的unittest应用是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》