==闭包 closure:
定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).
闭包是指调用了此函数外部嵌套函数作用域变量的函数;
闭包必须满足三个条件:(注意闭包不释放变量,有内存消耗)
1.必须有内嵌函数;
2.内嵌函数必须引用外部函数中的变量;
3. 外部函数返回值必须是内嵌函数;
示例closure.py #问题: #如何写一个函数,让此函数能通过调用参数y就能生成一个x的y次方的函数? #如下: def make_power(y): def fn(x): #注意此处为内嵌函数 return x ** y #内嵌函数引用外部嵌套函数的变量y return fn #外部函数返回值 为内嵌函数 pow2 = make_power(2) #此处返回的是fn函数对象 print('5的平方是:', pow2(5)) #25 pow3 = make_power(3) print('6的3次方是:', pow3(6)) #216
#一元二次方程式,已知x,求y值? #a * x ** 2 + b * x + c = y def get_fx(a, b, c): def fx(x): return a * x ** 2 + b * x ** 1 + c return fx f123 = get_fx(1, 2, 3) print('x=20, y=', f123(20)) print('x=50, y=',f123(50)) f654 = get_fx(6, 5, 4) print('x=20, y=', f654(20))
==装饰器decorators (专业提高篇)
问题:
函数名是变量,它绑定一个函数;
函数名 与 函数() 区别: 不加括号表示函数名变量绑定的函数;
加括号表达调用函数做事;
什么是装饰器:
装饰器是一个函数,主要作用是用来包装另一个函数或类(后面讲)
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等
应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
装饰器作用:
是在不改变原函数名(或类名)的情况下改变被包装对象的行为;
在不改变原函数和调用者行为的情况下,来改变原有函数功能;
函数装饰器:
函数装饰器是指装饰器是一个函数,传入的是一个函数,返回的也是一个函数;
语法:
def 装饰器函数名(参数):
语句块
return 函数对象
@装饰器函数名<换行>
def 函数名(形参列表):
语句块
import time #遵守开放封闭原则 #需求计算功能函数foo、bar运行时间 def show_time(f): #装饰器函数 def inner(): start = time.time() f() end = time.time() print('spend %s' % (end - start)) return inner #返回inner函数的内存地址 @show_time #等价于 foo = show_time(foo) def foo(): #功能函数 print('foo...') time.sleep(2) @show_time #等价于 bar = show_time(bar) def bar(): print('bar....') time.sleep(3) foo() bar()
# 示例见: mydeco1.py # ================================== def mydeco(fn): def fx(): print('fx函数被调用') return fx def myfunc(): print('函数myfunc被调用') #这样的写法可以用装饰器来代替 #等同于 # @mydeco # def myfunc()...... myfunc = mydeco(myfunc) myfunc() # ================================== # 修改后如下 # ================================== def mydeco(fn): def fx(): print('fx函数被调用') return fx #myfunc加了mydeco装饰器,等同于在myfunc创建之后调用赋值myfunc = mydeco(myfunc)语句 @mydeco #等价于 myfunc = mydeco(myfunc) def myfunc(): print('函数myfunc被调用') myfunc() # ======================================= #装饰器应用示例 def mydeco(fn): def fx(): print('++++++++++++') #要想在此处调用被装饰的函数myfunc怎么办? fn() #调用被装饰函数 print('------------------------') return fx ##添加装饰器,myfunc函数被装饰(@mydeco 等介于 myfunc = mydeco(myfunc) 代码) @mydeco #等价于 myfunc = mydeco(myfunc) def myfunc(): print('函数myfunc被调用') myfunc()
#看懂下面代码的调用关系及打印结果: def mydeco5(fn): print('装饰器函数被调用了....') #在第一次赋值时myfunction = mydeco5(myfunction)调用 def fx(): print('fx函数被调用') return fx @ mydeco5 #这里加装饰器和不加装饰器结果一样吗?为什么? def myfunction(): print('函数myfunction被调用') myfunction() myfunction() #调用第二次 myfunction() #调用第三次
======================== #mydeco5.py 示例 #此示例示意装饰器在不改变原函数和调用者行为的情况下,来改变原有函数功能; #再加一个装饰器用来添加余额变动提醒功能 #写一个装饰器函数用来发送短信 def send_message(fn): def fy(name, x): fn(name, x) #先办业务 print('发短信给', name, '办理了', x, '元') return fy #写一个装饰器函数 def privillage_check(fn): def fx(name, x): print('正在检查权限...') fn(name, x) #如果权限通过调用相应函数 return fx #写一个操作数据的函数(此函数用来示意存钱操作) # #注意此位置两层装饰器,层极关系是上到下层,层层嵌套调用 #(关系为:send_message在外层->privillage_check->savemoney在里层) @send_message @privillage_check def savemoney(name, x): print(name, '存钱', x, '元') # @privillage_check def withdraw(name, x): print(name, '取钱', x, '元') # savemoney('小张', 200) savemoney('小赵', 200) withdraw('小李', 500) =========================
带参数的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。
1 import time 2 3 def time_logger(flag=0): 4 5 def show_time(func): 6 7 def wrapper(*args,**kwargs): 8 start_time=time.time() 9 func(*args,**kwargs) 10 end_time=time.time() 11 print('spend %s'%(end_time-start_time)) 12 13 if flag: 14 print('将这个操作的时间记录到日志中') 15 16 return wrapper 17 18 return show_time 19 20 21 @time_logger(3) 22 def add(*args,**kwargs): 23 time.sleep(1) 24 sum=0 25 for i in args: 26 sum+=i 27 print(sum) 28 29 add(2,7,5)
@time_logger(3) 做了两件事:
(1)time_logger(3):得到闭包函数show_time,里面保存环境变量flag
(2)@show_time :add=show_time(add)
上面的time_logger是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当我 们使用@time_logger(3)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。
1 实例: 2 import os 3 import sys 4 5 # 京东网站有以下home 、finance、book几个版块 6 # home() 7 # finance() 8 # book() 9 # 打开会选展示home首页 10 # 当在选择finance页面时,需要检测有没有登录,没有登录就调用登录验证接口,要求用weixin方式验证 11 # 当选择book页面时,需要检测有没有登录,没有登录就调用登录验证接口,要求用jidong方式验证 12 13 #文件读取用户名与密码函数 14 def read_info(filename): 15 local_path = sys.path[0] 16 # 拼接localpath与filename 17 localxmlpath = os.path.join(local_path, filename) 18 # open文件,按行读取,返回content1列表 19 with open(localxmlpath, 'r') as f1: 20 content1 = f1.readlines() 21 return content1 22 23 user_status = False #定义变量记录用户登录状态,用户登录后把这个修改为True 24 25 def login(auth_type): #把要选择的认证方式通过auth_type传入 26 def auth(func): 27 def inner(*args): #再定义一层函数 28 print('+++++ %s +++++++' % (func)) 29 pass_list = read_info(auth_type+'.txt') #根据auth_type拼接需要读取的文件名 30 _username = pass_list[0].rstrip(' ') #读取的用户名,去除字符串最后的' '字符 31 _password = pass_list[1] #读取密码 32 33 global user_status #声明记录用户状态的变量为全局变量 34 35 if not user_status: 36 username = input('username:') 37 password = input('password:') 38 39 if username == _username and password == _password: 40 print('成功登录,welcome!!!') 41 user_status = True #登录后改变登录状态标识为true 42 func(*args) #验证通过后,执行功能函数 43 else: 44 print('woring username or passwd!') #验证未通过提示错误信息 45 else: 46 print('用户已经登录,无需验证') 47 func(*args) 48 return inner #返回内部函数的内存引用id 49 return auth 50 51 52 def home(): 53 print('welcom to home page') 54 55 @login('weixin') #1、带入weixin参数给auth_type变量,2、finance = auth(finance) 56 def finance(): 57 print('welcom to finance page') 58 59 @login('jidong') #1、带入jidong参数给auth_type变量,2、book = auth(book) 60 def book(style): 61 print('welcom to book page') 62 63 64 65 home() 66 67 68 book('3x') 69 70 finance()
多层装饰器:
#定义函数:完成包裹数据 def makeBold(fn): def wrapped(): print('----1----') return '<b>' + fn() + '</b>' return wrapped #定义函数:完成包裹数据 def makeItalic(fn): def wrapped(): print('----2----') return '<i>' + fn() + '</i>' return wrapped @makeBold @makeItalic def test3(): print('------3-----') return 'hello world.3' ret = test3() print(ret) #执行结果如下: ----1---- ----2---- ------3----- <b><i>hello world.3</i></b>
==函数的文档字符串:
函数内第一次末赋值给任何变量的字符串是此函数的文档字符串
语法:
def 函数名(形参列表):
'''函数的文档字符串'''
函数语句块
示例:
def hello():
'''此函数来打招呼
这是函数的文档字符串
'''
pass
说明:
1. 文档字符串通常用来说明本函数的功能和使用方法;
2. 在交互模式下,输入help(函数名),可以查看函数的文档字符串;
3. 文档字符串需要被解析执行器解析并绑定变量的,注释是被解析执行器忽略的。
函数的__doc__属性:
__doc__属性用于记录文档字符串;
函数的__name__属性:
__name__属性用于记录函数的名称;
==函数的定义语法:
@装饰器1
@装饰器2
......
def 函数名([[[[位置形参],*元组形参(或*)], 命名关键字形参], **字符形参])
'''文档字符串'''
语句块
# 面试题,思考? L = [1, 2, 3] def f(n=0, lst=[]): #注意此处不是每次函数调用赋值,而lst列表是存在于函数内部,生命周期与函数一致 lst.append(n) print(lst) f(4, L) #打印结果是什么?[1, 2, 3, 4] f(5, L) #打印结果是什么?[1, 2, 3, 4, 5] f(100) #[100] f(200) #打印结果是什么?为什么?[100, 200] #如下代码的打印结果是什么? L = [1, 2, 3] def f(n = 0, lst = None): if lst is None: lst = [] #此处是调用时赋值 lst.append(n) print(lst) f(4, L) #打印结果是什么?[1, 2, 3, 4] f(5, L) #打印结果是什么?[1, 2, 3, 4,5] f(100) #[100] f(200) #打印结果是什么?为什么?[200];因为调用函数时,未带入列表;函数lst默认参数为None,if判断会对lst赋值为[];