python作用域的理解-python中对变量的作用域LEGB、闭包、装饰器基本理解
一、作用域
在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域。python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。
在Python中并不是所有的语句块中都会产生作用域。只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。而在if、for、while等语句中不产生作用域。
L(local)局部作用域,存在于def内的局部性的作用域,随着函数的定义而产生。
E(enclosing)嵌套作用域,同L作用域是相对而言,存在函数嵌套时,内部函数的作用域称为L作用域,外部函数称为E作用域。
G(global)全局作用域,变量在该模块内的作用域,可以简单的理解为单个文件内的范围。
B(built-in)内置作用域,系统内固定模块里定义的变量,如预定义在builtin 模块内的变量,最高级别的作用域,范围最广,可以理解为当前运行系统的范围。
举例说明:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = "问道编程"
__date__ = "2018-06-13 15:40"
passline = 60 #global var
def func(val):
passline = 90 #for func, it"s local var;for in_func, it"s enclosing var
if val >= passline:
print "pass"
else:
print "fail"
def in_func():
print val
return in_func
def Max(val1, val2):
return max(val1, val2) # max is a built-in fun,函数也可以赋值给变量,所以所有的内置函数的作用域都可视作B
声明全局变量:
a = 100
def foo():
global a # 引用全局变量a,并认可其修改的值
a = 200
print(a)
foo()
print(a)
---------
100
200
二、闭包
首先理解下python中的函数,在python中,函数是一个对象(可以通过type函数查看),在内存中占用空间;函数执行完成之后内部的变量会被解释器回收,但是如果某变量被返回,则不会回收,因为引用计数器的值不为0;既然函数也是一个对象,他也拥有自己的属性;对于python函数来说,返回的不一定是变量,也可以是函数。
由此引出闭包的概念,当存在函数嵌套时(如上例中的func()和in_func()就是嵌套关系),外部函数的返回值为内部函数,且内部函数引用外部函数的变量、参数,并将引用的变量、参数封装在函数中一起作为返回值,其中的内部函数就称为一个闭包。
形成闭包的必要条件:
1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3)外部函数必须返回内嵌函数——必须返回那个内部函数
三、闭包的使用场景
第一个例子:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = "问道编程"
__date__ = "2018-06-13 15:53"
def funx():
x=5 # 对于funx,是L作用域,对于funy,是E作用域
def funy(): # 是一个闭包
nonlocal x # 绑定到外部的x,只在python3中使用
x+=1
return x
return funy
a = funx()
print(a())
print(a())
print(a())
print(x)
-------------------------
6
7
8
NameError: name "x" is not defined
本来x只是funx()的局部变量,但是形成了闭包之后,它的行为就好像是一个全局变量一样,最后的错误说明x并不是一个全局变量。其实这就是闭包的一个十分浅显的作用,形成闭包之后,闭包变量能够随着闭包函数的调用而实时更新,就好像是一个全局变量那样。
第二个例子,也是讲闭包用的最多的例子:
def func_150(val): # 总分为150时,及格分数为90
passline = 90
if val >= passline:
print "pass"
else:
print "fail"
def func_100(val): # 总分为100时,及格分数为60
passline = 60
if val >= passline:
print "pass"
else:
print "fail"
func_100(89)
func_150(89)
使用闭包优化上面的代码:
def set_passline(passline):
def cmp(val):
if val >= passline:
print "pass"
else:
print "fail"
return cmp
f_100 = set_passline(60) # f_100调用函数set_passline(),并将60赋值给变量passline,这是f_100等于函数的返回值,也就是函数cmp
f_150 = set_passline(90)
f_100(89) # f_100()=cmp(),将89赋值给val,运行cmp()函数,输出结果
f_150(89)
第三个例子:
def my_sum(*arg):
print("in my_sum,arg=",arg)
return sum(arg)
def dec(func):
def in_dec(*arg):
print("in in_dec,arg=", arg)
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return func(*arg)
return in_dec
my_sums = dec(my_sum) # 命名为my_sums,是为了和my_sum进行区分,便于理解
# ①调用dec()函数,将dec的返回值赋值给my_sums,相当于my_sums=in_dec,②将函数my_sum赋值给func,相当于func=my_sum
result = my_sums(1, 2, 3, 4, 5)
# ③相当于将(1, 2, 3, 4, 5)赋值给in_dec函数中的arg,调用并执行函数in_dec(),
# ④in_dec()函数的return返回值是func()函数,将in_dec函数中的arg赋值给func()函数中的arg,也就是赋值给my_sum()中的arg
# ⑤调用并执行函数my_sum(),将sum(arg)结果返回给变量result
print(result)
------------
in in_dec,arg= (1, 2, 3, 4, 5)
in my_sum,arg= (1, 2, 3, 4, 5)
15
整个过程可以简单的理解为,先运行dec()函数,其返回值是in_dec函数,再运行in_dec函数,其返回值为func函数,也就是my_sum函数,再运行my_sum函数
dec() -> in_dec() -> my_sum()
第四个例子:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = "问道编程"
__date__ = "2018-06-13 17:08"
variable = 300
def test_scopt():
print(variable) # variable是test_scopt()的局部变量,但是在打印时并没有绑定内存对象。
variable = 200 # 因为这里,所以variable就变为了局部变量
test_scopt()
print(variable)
------------
UnboundLocalError: local variable "variable" referenced before assignment
上面的例子会报出错误,因为在执行程序时的预编译能够在test_scopt()中找到局部变量variable(对variable进行了赋值)。在局部作用域找到了变量名,所以不会升级到嵌套作用域去寻找。但是在使用print语句将变量variable打印时,局部变量variable并有没绑定到一个内存对象(没有定义和初始化,即没有赋值)。本质上还是Python调用变量时遵循的LEGB法则和Python解析器的编译原理,决定了这个错误的发生。所以,在调用一个变量之前,需要为该变量赋值(绑定一个内存对象)。
注意:为什么在这个例子中触发的错误是UnboundLocalError而不是NameError:name "variable’ is not defined。因为变量variable不在全局作用域。Python中的模块代码在执行之前,并不会经过预编译,但是模块内的函数体代码在运行前会经过预编译,因此不管变量名的绑定发生在作用域的那个位置,都能被编译器知道。Python虽然是一个静态作用域语言,但变量名查找是动态发生的,直到在程序运行时,才会发现作用域方面的问题
可以使用global将变量variable声明为全局变量。
四、装饰器
概念性的知识,拗口的表述:
1.装饰器就是对闭包的使用;
2.装饰器的作用是装饰函数;
3.装饰器会返回一个函数对象,被装饰的函数接收;
4.被装饰函数标识符指向返回的函数对象。
将上面第三个例子用上装饰器:
def dec(func):
def in_dec(*arg):
print("in in_dec,arg=", arg)
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return func(*arg)
print("in dec") # 新增的一行
return in_dec
@dec
def my_sum(*arg):
print("in my_sum,arg=",arg)
return sum(arg)
print(type(my_sum))
对概念的实例化:
1、@dec是一个装饰器,是调用dec()函数形成的装饰器;
2、被装饰的函数是my_sum(),即调用dec()函数形成装饰器时,dec()函数的参数为my_sum()函数,即:func = my_sum
3、装饰器的返回值是函数in_dec(),该返回值被函数my_sum()接收,其实是传给被装饰函数(即my_sum()函数)的标识符my_sum,即:my_sum=in_dec
直接运行该文件,输出:
--------------
in dec
# 此时的my_sum并非指定义的def my_sum(*arg)函数,而是变成接收了装饰器的返回值,in_dec函数。
说明只要使用了装饰器,就会调用装饰器里的函数;并且装饰器的返回值:in_dec(),被my_sum接收(如果没有return in_dec,直接运行,则my_sum格式为:,说明装饰器没有返回值时,会返回None,这一点跟函数一样)。
接下来,执行语句:
result = my_sum(1, 2, 3, 4, 5)
print(result)
------------
in dec
in in_dec,arg= (1, 2, 3, 4, 5)
in my_sum,arg= (1, 2, 3, 4, 5)
15
运行过程为(结合第三个例子进行理解):
1、@dec,调用dec函数,其参数就是被装饰的函数my_sum(),即func=my_sum,执行print("in dec"),返回in_dec函数,被my_sum接收,即:最后会将函数in_dec的运行结果,传给被装饰函数的标识符my_sum;
2、调用in_dec函数,参数为(1,2,3,4,5),执行print、if、for语句,执行return func(*arg),func=my_sum函数;
3、调用函数my_sum(),参数为(1,2,3,4,5),执行print语句,执行return sum(*arg),返回最终结果;
参考文章:
https://www.cnblogs.com/fireporsche/p/7813961.html
https://www.cnblogs.com/cotyb/p/5243252.html
python作用域的理解-python中对变量的作用域LEGB、闭包、装饰器基本理解相关推荐
- SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理
SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理 本文连接 简介 SIGIA_4P 网址 a. 课程OKR Objectives and ...
- Python(三)对装饰器的理解
装饰器是 Python 的一个重要部分,也是比较难理解和使用好的部分.下面对装饰器做一下简单整理 1. 前言 装饰器实际上是应用了设计模式里,装饰器模式的思想: 在不概念原有结构的情况下,添加新的功能 ...
- python装饰器哪个好_[Python] 对 Python 装饰器的理解心得
最近写一个py脚本来整理电脑中的文档,其中需要检校输入的字符,为了不使代码冗长,想到使用装饰器. 上网搜索有关python的装饰器学习文档,主要看的是AstralWind的一篇博文,以及Limodou ...
- python装饰器-如何理解Python装饰器?
我从以下几点,由浅入深详细讲解一下Python装饰器:什么事装饰器? 为什么用装饰器? 在哪里用装饰器? 然后以示例+讲解相结合的方式阐述,同时会讲解一些在很多教程和书籍中不会涉及到的内容. 什么是P ...
- 趁着课余时间学点Python(九)函数的进阶 生成器 装饰器的理解
文章目录 前言 生成器 为什么使用 格式 嵌套函数 装饰器 使用场景 使用 结语 前言 相信大家已经对函数有了一定的理解了吧,那么来看看生成器和装饰器吧 生成器 按道理来说,生成器应该是和迭代器一起讲 ...
- python入门day11闭包装饰器
目录 闭包 例子 同级闭包 装饰器引入 装饰器使用 无参例子 有参例子 可变参数例子 带关键字参数的装饰器 双层装饰器 装饰器带参数 装饰器的应用 闭包 def func():a=100def inn ...
- python高阶函数闭包装饰器_Python_基础_(装饰器,*args,**kwargs,高阶函数,函数闭包,函数嵌套)...
一,装饰器 装饰器:本质就是函数,功能是为其它的函数动态添加附加的功能 原则:对修改关闭对扩展开放 1.不修改被修饰函数的源代码 2.不修改被修改函数的调用方式 装饰器实现的知识储备:高阶函数,函数嵌 ...
- python装饰器与闭包_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。...
转载:http://python.jobbole.com/81683/ 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需 ...
- python闭包和装饰器的区别_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。(转)...
呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需要你了解一些函数式编程的概念,当然还有理解在python中定义和调用函数 ...
最新文章
- C#趣味程序---个位数为6,且能被3整出的五位数
- 基于OpenCV的条形码区域分割
- python多线程的几种方法
- Linux 环境下NFS 服务搭建
- [2018雅礼集训1-16]方阵
- Java自学笔记(13):【面向对象】方法覆盖,final关键字,对象转型
- UDT协议实现分析——连接的建立
- 『数学』你确定你学会了勾股弦定理!真的吗?看完这个篇文章再回答我!
- redis出现过多command 慢查询slowlog出现command命令
- python做线性回归统计推断提取参数_概率分析方法与推断统计(来自我写的python书)...
- 旷视科技IPO过会,AI技术“立业”难言轻松
- python二维向量运算模拟_Python数学基础之向量定义与向量运算(附代码)
- Java继承中为什么不可以降低父类重写方法的访问权限
- 常用图表的用法-分布类
- 寻找中项和第k小元素c语言,寻找一个序列中第k小的元素——分治法
- 电脑上没有tts信息服务器,TTS——让你的电脑会说话-win7 tts
- 安规电容知识详解,X电容和Y电容
- 《 种子用户方法论》读书笔记
- 数值计算中的overflow and underflow
- 出中的意思是什么_回归分析中的“回归”是什么意思?