python:作用域和闭包

作用域

在Python程序中声明、改变、查找变量名时,都是在一个保存变量名的命名空间中进行中,此命名空间亦称为变量的作用域。
python的作用域是静态的,在代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。
变量作用域之LENGB

L = Local 局部作用域
E = Enclosing 嵌套作用域
N = nonlocal 只作用于嵌套作用域,而且只是作用在函数里面
G = global 全局作用域
B = Built-in 内置作用域

python引用变量的顺序:

当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量.

L = Local 局部作用域

局部变量包含在def定义的函数体内。在函数体内声明的变量,默认都是局部变量,除非有特别说明,如全局变量的声明要用关键字global.

def testlocal():
    x = 123 # x即为局部变量
print(x)

以上代码运行就会报NameError, 因为x是个局部变量,在函数外要print(x), 按照上面python引用变量的顺序,是找不了x变量的,故为报错.

NameError: name 'x' is not defined

再比如:

x = 123
def testlocal():
    print(x)
    x = 100
 
testlocal()

这时以上代码运行就会报错.

UnboundLocalError: local variable 'x' referenced before assignment

什么原因?新手常会遇到这类问题, 因为按照python引用变量的顺序,当print(x)时,就会先在函数体内查找x,能找到,但在他的下一行,故为在声明前被引用是不对的.

G = global 全局作用域

还是刚才的例子,可以在print调用x之前将要找的x指明为global变量解决:

x = 123
def testGlobal():
	global x
    print(x)
    x = 100
    
testGlobal()

结果为

123

N = nonlocal的用法

这个一般是用在闭包函数里。实例如下:

x = 123
def outer():
    x = 100
    def inter():
        x = 200
    inter()
    print(x)
    
func()

运行的结果为100, 那如果要求inter中对x的修改是有效的,必须加上关键字nonlocal:

x = 123
def outer():
    x = 100
    def inter():
        nonlocal x # 指明要修改的x不是local,而是non-local
        x = 200
    inter()
    print(x)
    
func()

其它

python变量的作用域只在作用域内有效,需要注意的是,在if-elif-else, for-in, while, try-except-finally这些关键字中并不会产生特定的作用域,如下:

#!/user/bin/python3
#-*- coding:utf-8 -*-
 
for i in range(10):
    i = i + 1
print(i)
>>>10

运行结果并不会报错,会输入i, 这里的i就是一个全局变量,这是python的一个特点,如果在C/java中这种写法就会报错,会报变量i未声明.

闭包——装饰器的本质也是闭包

“闭包”的本质就是函数的嵌套定义,即在函数内部再定义函数。

“闭包”有两种不同的方式,第一种是在函数内部就“直接调用了”;第二种是“返回一个函数名称”。

第一种形式——直接调用

def Maker(name):
	num=100
	def func1(weight,height,age):
		weight+=1
		height+=1
		age+=1
		print(name,weight,height,age)
	func1(100,200,300) #在内部就直接调用“内部函数”

Maker('feifei') #调用外部函数,输出 feifei 101 201 301

第二种形式——返回函数名称

def Maker(name):
	num=100
	def func1(weight,height,age):
		weight+=1
		height+=1
		age+=1
		print(name,weight,height,age)
	return func1 #此处不直接调用,而是返回函数名称(Python中一切皆对象)

maker=Maker('feifei') #调用包装器
maker(100,200,300) #调用内部函数

“闭包”的作用——保存函数的状态信息,使函数的局部变量信息依然可以保存下来

def Maker(step): #包装器
    num=1
    def fun1(): #内部函数
        nonlocal num #nonlocal关键字的作用和前面的local是一样的,如果不使用该关键字,则不能再内部函数改变“外部变量”的值
        num=num+step #改变外部变量的值(如果只是访问外部变量,则不需要适用nonlocal)
        print(num)
    return fun1

#=====================================#
j=1
func2=Maker(3) #调用外部包装器
while(j<5):
    func2() #调用内部函数4次 输出的结果是 4、7、10、13
    j+=1

从上面的例子可以看出:

  • 外部装饰器函数的局部变量num初始时值是1
  • 调用装饰器Maker(3)时候传入的参数step=3都被num记忆了下来,所以才有1+3=4、4+3=7、7+3=10、10+3=13,而不是每次都输出4。

从这里可以看出,Maker函数虽然调用了,但是它的局部变量信息却被保存了下来,这就是“闭包”的最大的作用——保存局部信息不被销毁。

一个例子:

def urltemplate(template):
	def opener(**kwargs):
		return urlopen(template.formate_map(kwargs))
	return opener

yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo(names='IBM,AAPL,FB',fields='sllclv'):
	print(line.decode('utf-8'))

首先调用urltemplate函数,传入模板,并将返回值赋给一个变量
然后在需要的时候传入参数即可
这里使用闭包的目的就是让函数记住模板,方便以后的调用

闭包内部变量的访问和修改

一般来说,在闭包内层定义的变量对于外界来说是完全隔离的。但是,可以通过编写存取函数(accessor function,即getter/setter方法),并将它们作为函数属性附加到闭包上来提供对内层变量的访问和支持。

def sample():
	n = 0
	# Closure function
	def func():
		print('n=',n)
	
	# Accessor methods for n
	def get_n():
		returnn = n

	def set_n(value):
		nonlocal n
		n = value
	
	# Attach as function attributes
	func.get_n = get_n
	func.set_n = set_n
	return func

使用:

>>>f = sample()
>>>f()
n= 0
>>>f.set_n(10)
>>>f()
n= 10
>>>f.get_n()
10

这里的技术要点:

  • nonlocal使得编写函数来修改内层变量成为可能
  • 函数属性能够将存取函数以直接的方式附加到闭包函数上,它们工作起来很像实例的方法
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值