这篇文章主要介绍了Python变量作用域LEGB用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
闭包就是, 函数内部嵌套函数. 而 装饰器只是闭包的特殊场景而已, 特殊在如果外函数的参数是指向一个, 用来被装饰的函数地址时(不一定是地址哈, 随意就好) , 就有了 "@xxx" 这样的写法, 还是蛮有意思的. 装饰器的作用是 在不改变原函数的代码前提下, 额外给原函数填写新功能. 写法上来看, 还是比较简洁优雅的.
装饰器的通俗写法
# 装饰器的通用写法 def out(func): def inner(*args, **kwargs): print("we are checking...", args[0]) return func(*args, **kwargs) return inner @out def check_2019_nCov(name): return f"now, {name} is very healthy..." tmp = check_2019_nCov('youge') print(tmp) # output we are checking... youge now, 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) # output get 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 是 global var1 = 666 def 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) # output youge
发现没有能改掉, 这是自然的. 因为, 在调用函数时, 里面的 name 是一个 Local 变量, 是不会影响到全局的 name的, 如果想实现在 在函数内部来改变 全局变量, 则将 该变量用 global 关键字声明即可.
name = "youge" def change_name(): global name name = "youyou" # 希望将 "youge" 改为 "youyou" change_name() print(name) # output youyou
很简单, 在函数内部, 用 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() # output global: jack local: youyou encolse: 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() # output global: jack local: youyou encolse: youyou
函数嵌套场景中, 通过 在 local 层, 声明 name 为 nonlocal 则将 enclosed 层的name改掉了. 但如果在 local 层 声明 global 则是没有其效果的, 为啥, 嗯... 暂时还不清楚, 也是实验的, 暂时.
哦, 突然想贴一个, 我还是菜鸟时常, 犯的小错误:
name = 'youge' def change_name(): name = name + "youyou" change_name() print(name) # output UnboundLocalError: 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) # output yougeyouyou
# 方式2: 声明为全局即可, 不推荐 name = 'youge' def change_name(): global name name = name + "youyou" change_name() print(name) # output yougeyouyou
小结
- 闭包, 装饰器的本质是函数的嵌套, 参数及函数能被传递的原因是, Pyhton变量的本质是之指针
- Python中用 命名空间 来 解决 变量名冲突, 原理跟 计算机系统(如 Linux) 存储文件是一样的逻辑
- 变量名寻找的规则为 Local -> Enclosed -> Global -> Built-in
- 个人觉得能理解,全局与局部的"相对性" 即可, 另外, 可用 global 与 nonlocal (E层) 改变变量作用等级.
- 变量作用域, 一直在用, 但却经常忽略它, 这里做个总结, 没事常翻翻, 作用域, 就到这吧.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 明达年度发烧碟MasterSuperiorAudiophile2021[DSF]
- 英文DJ 《致命的温柔》24K德国HD金碟DTS 2CD[WAV+分轨][1.7G]
- 张学友1997《不老的传说》宝丽金首版 [WAV+CUE][971M]
- 张韶涵2024 《不负韶华》开盘母带[低速原抓WAV+CUE][1.1G]
- lol全球总决赛lcs三号种子是谁 S14全球总决赛lcs三号种子队伍介绍
- lol全球总决赛lck三号种子是谁 S14全球总决赛lck三号种子队伍
- 群星.2005-三里屯音乐之男孩女孩的情人节【太合麦田】【WAV+CUE】
- 崔健.2005-给你一点颜色【东西音乐】【WAV+CUE】
- 南台湾小姑娘.1998-心爱,等一下【大旗】【WAV+CUE】
- 【新世纪】群星-美丽人生(CestLaVie)(6CD)[WAV+CUE]
- ProteanQuartet-Tempusomniavincit(2024)[24-WAV]
- SirEdwardElgarconductsElgar[FLAC+CUE]
- 田震《20世纪中华歌坛名人百集珍藏版》[WAV+CUE][1G]
- BEYOND《大地》24K金蝶限量编号[低速原抓WAV+CUE][986M]
- 陈奕迅《准备中 SACD》[日本限量版] [WAV+CUE][1.2G]