Python变量作用域LEGB用法解析
这篇文章主要介绍了Python变量作用域LEGB用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
闭包就是,函数内部嵌套函数.而装饰器只是闭包的特殊场景而已,特殊在如果外函数的参数是指向一个,用来被装饰的函数地址时(不一定是地址哈,随意就好),就有了"@xxx"这样的写法,还是蛮有意思的.装饰器的作用是在不改变原函数的代码前提下,额外给原函数填写新功能.写法上来看,还是比较简洁优雅的.
装饰器的通俗写法
#装饰器的通用写法 defout(func): definner(*args,**kwargs): print("wearechecking...",args[0]) returnfunc(*args,**kwargs) returninner @out defcheck_2019_nCov(name): returnf"now,{name}isveryhealthy..." tmp=check_2019_nCov('youge') print(tmp) #output wearechecking...youge now,yougeisveryhealthy...
给装饰器传参
虽然这种"@"的写法,是要求外函数的参数是一个func地址,但要达到可以传参,只要再在外面包一层函数(作用是接受参数),这样不就相当于扩大作用空间,拿到参数了呀.
#最外层的函数作用是,给装饰器传递参数 defget_param(*args,**kwargs): defout(func): definner(*args,**kwargs): print("getparams",args,kwargs) returnfunc(*args,**kwargs) returninner returnout @get_param("youge") defcheck_2019_nCov(name): returnf"now,{name}isveryhealthy..." tmp=check_2019_nCov("youge") print(tmp) #output getparams('youge',){} now,yougeisveryhealthy...
这种个装饰器传递参数的应用场景,在Web应用中,以Flask为例,就是所有的路由url的概念呀,如route("/login")这样的写法,其原理就是用各种装饰器来实现路由->视图的映射关系的.
仔细一看,整个过程忽略了一个重要的话题,即命名空间,及变量的作用域,或者说命名空间如怎样的.
LEGB法则
命名空间
前篇已经详细阐述过了,Python变量的本质是指针,是对象的引用,而Python中万物皆对象.这个对象是真正存储数据的内存地址,是各种类(数据类型,数据结构)的实例.(变量就是用来引用对象的)差不多这个意思吧.
最为直观的解释:
"Anamespaceisamappingfromnamestoobjects".(变量名和对象的映射)
"MostnamespacesarecurrentlyimplementedasPythondictionaries."(大部分命名空间通过字典来实现)
即命名空间是用来避免变量命名冲突的约束.各个命名空间是彼此独立的,一个空间中不能重名,不同空间中是不没有关系的.就跟计算机系统,存储文件是样的逻辑.
foriinrange(10): print(i) #这两句话都用到了i但其各自的空间是不一样的. [iforiinrange(100)]
- 内置空间:(built-innames):Python内置名称,如内置函数,异常类...
- 全局空间:(globalnames):常量,模块中定义的名称(类,导入模块)...
- Enclosed:可能嵌套在函数内的函数等...
- 局部名称:(localnames):函数中定义的名称(函数内的变量)...
Python查找变量顺序为:Local->Enclosed->Global->Built-in。
其实,从我个人经验而言,能区分局部和全局的相对性.就好了,基本上.直观上,以一个写代码的py文件为例.最外层有,变量,类定义,函数定义,从from..import..的变量或函数名,这些就是全局变量,最外面的类或者函数,里面是各自的名字空间呀.
#var1是global var1=666 deffoo(): #var2是局部 var2=666 deffoo2(): #内嵌的局部 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#全局 defsum(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" defchange_name(): name="youyou" #希望将"youge"改为"youyou" change_name() print(name) #output youge
发现没有能改掉,这是自然的.因为,在调用函数时,里面的name是一个Local变量,是不会影响到全局的name的,如果想实现在在函数内部来改变全局变量,则将该变量用global关键字声明即可.
name="youge" defchange_name(): globalname name="youyou" #希望将"youge"改为"youyou" change_name() print(name) #output youyou
很简单,在函数内部,用global将其声明为全局变量即可.同样,针对于**函数嵌套,即向闭包,装饰器等,通过关键字nonlocal实现将函数内的变量,声明为函数外的Enclose层**
name="jack" defouter(): name="youge" #函数内有一个local函数空间 definner(): 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" defouter(): name="youge" #函数内有一个local函数空间 definner(): nonlocalname 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' defchange_name(): name=name+"youyou" change_name() print(name) #output UnboundLocalError:localvariable'name'referencedbeforeassignment
原因就在于,在函数内部的空间中,对name是没有定义的.在Python中,对于函数过程的存储,是通过递归栈实现的.利用栈的FILO,(先进后出)的特点,当遇到一个函数,就用栈将其参与的成员,依次入栈,如有return则将置为栈元素.
变量要先定义,后使用嘛,Python中的定义是指,该变量指向某个实例对象即可,而非其它语言中的类型声明哦,这里最容易混淆.
修改name为全局变量,通过函数参数传递即可:
#方式1:定义个单独的函数来处理 name='youge' defchange_name(s): name=s+"youyou" print(name) #全局变量来传递给函数空间,即"先定义,后执行") change_name(name) #output yougeyouyou
#方式2:声明为全局即可,不推荐 name='youge' defchange_name(): globalname name=name+"youyou" change_name() print(name) #output yougeyouyou
小结
- 闭包,装饰器的本质是函数的嵌套,参数及函数能被传递的原因是,Pyhton变量的本质是之指针
- Python中用命名空间来解决变量名冲突,原理跟计算机系统(如Linux)存储文件是一样的逻辑
- 变量名寻找的规则为Local->Enclosed->Global->Built-in
- 个人觉得能理解,全局与局部的"相对性"即可,另外,可用global与nonlocal(E层)改变变量作用等级.
- 变量作用域,一直在用,但却经常忽略它,这里做个总结,没事常翻翻,作用域,就到这吧.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。