1.函数作用域
函数作用域LEGB,L>E>G>B
- L : local 函数内部作用域
- E : enclosing 函数内部与内嵌函数之间
- G :global 全局作用域
- B : build-in 内置作用域
变量和函数查找
passline = 60
def checkvalue(val):
passline = 90
if val > passline:
print('pass')
else:
print('failed')
def func()
val = str(val)
print(val)
func(val)
checkvalue(93)
python运行时fun()函数时,def内部即是local,
checkvalue()内部即是enclosing
第一行函数体外定义的passline即是global
str函数即是bulid-in 内置作用域
2.python闭包
closure:内部函数中对enclosing作用域的变量进行引用
函数实质与属性
- 函数是一个对象
- 函数执行后完成内部变量回收
- 函数属性
- 函数返回值
继续引用上述代码
def check(val):
passline = 100
#以16进制打印val的id值
print('%x'%id(val))
if val > 60:
print('pass')
else:
print('fail')
def func():
print(val)
func()
return func
f = check(90)
当调用f = check(90)时,输出结果为
1009dd4a0
pass
90
此时可看出val的id值为1009dd4a0
然后再执行:f()
90
按照python的变量回收机制,val在check函数执行完成后,应该被回收掉,为什么会出现调用f()时,出现val的值呢
此时再执行:
print(f.__closure__)
输出值为
(<cell at 0x105fed6d8: int object at 0x1009dd4a0>,)
由此可看出,f中存在int类型的val值的引用,因为python采用引用计数的垃圾回收机制,当check()函数执行完成后,val对象被保存在func的函数属性中,仍存在引用,所以并没有对val进行垃圾回收。
闭包
闭包实质上是一个函数,它使得函数在执行后,函数的返回对象不会被内存回收,闭包中包含着自由变量,只要闭包能被访问到,自由变量就可以被访问。
在上述代码中f = check()就是一个闭包
每个函数都有一个_closuer_属性,如果这个函数是一个闭包的话,那么这个函数的返回值就是一个由cell对象组成的元组,cell对象中的cell_contents属性值就是闭包中的自由变量
测试代码:
c = f.__colsure__[0].cell_contents
#90
可以看到c的值为90
val的值被储存在闭包对象的cell_contents中,所以在闭包函数外部可以访问函数内部变量
闭包的好处:
1. 代码的复用
2. 封装
3.装饰器
- 装饰器用来装饰函数
- 返回一个函数对象
- 被装饰函数标识符指向返回的函数对象
- 语法糖 @
装饰器是对闭包的一种实现方式
假设有两个班,1班人数为40人,二班人数为30人,每天需要对两个班的考勤做出统计,
考勤规则为:出勤的人数大于班级人数的80%,考勤则通过,否则不通过。
题目:设计考勤函数
- 使用两个函数分别考勤
#1班考勤办法
def check_1(num):
##班级人数
total = 40
if num > total * 0.8:
print('ok')
else:
print('fail')
#2班考勤办法
def check_2(num):
total = 30
if num > total * 0.8:
print('ok')
else:
print('fail')
突然有一天,增加需求,需要对每个班的人数进行判断,如果这个班到的人数少于10人,考核为不及格。这时候如果班级数量很多,就需要重复去写相同的代码,此时就需要装饰器
#将原本每个班的判断函数传入
def dec(func):
def _wrapper(num):
if num < 10:
print('不及格')
return func(num)
return _wrapper
#为原本函数加上装饰器
@dec
def check_2(num):
total = 30
if num > total * 0.8:
print('ok')
else:
print('fail')
print(check_2(2))
#---> 不及格
#---> fail