python基础16_闭包_装饰器
不了解是否其他语言也有类似 python 装饰器这样的东西。
最近才发现ECMAScript6也是有生成器函数的,也有 yield generator
装饰器的基础知识是闭包:
# 闭包:嵌套函数,内部函数调用外部函数的变量 def outer(): a = 1 def inner(): print(a) # __closure__ 可以用来判断闭包 # print(inner.__closure__) # 打印 cell at ... 则表明它是一个闭包 return inner # 返回内部函数的内存地址,因为此函数用到了外部函数的变量,所以外部函数的变量也不会因函数的调用结束而消失 inn = outer() inn() from urllib.request import urlopen # ret = urlopen('http://www.xiaohua100.cn/index.html').read() # print(ret) # def get_url(): # url = 'http://www.xiaohua100.cn/index.html' # ret = urlopen(url).read() # print(ret) # # get_url() # 闭包的简单示例: # def get_url(): # url = 'http://www.xiaohua100.cn/index.html' # def get(): # ret = urlopen(url).read() # print(ret) # return get # 得到 url 结果 # # get_func = get_url() # 将结果赋给变量,保存下来。 # get_func() #
装饰器的作用与原则
import time def say_hello(): time.sleep(1) print('hello, everyone...') # 定义一个闭包函数来增加功能 def tmr(f): # 装修器函数 def inner(): start = time.time() f() # 被装饰函数 end = time.time() print(end - start) return inner # 传参数的名字,不加括号 fun = tmr(say_hello) # 使用闭包函数 fun() # 装饰器的作用 —— 不想修改函数的调用方式 但是还想在原来的函数前后添加功能 # timmer就是一个装饰器函数,只是对一个函数 有一些装饰作用 # 原则: 开放封闭原则 # 开放 : 对扩展是开放的 # 封闭 : 对修改是封闭的 def deco(f): def inner(*args, **kwargs): print('装饰器在函数之前') ret = f(*args, **kwargs) print("装饰器在函数之后") return inner @deco def say_hi(): time.sleep(1) print('Hi, good morning ...') say_hi()
#!/usr/bin/env python # coding:utf-8 # 复习一下函数作为参数传递。 # def f11(): # print(12345) # # # def f12(xxx): # xxx() # # # f12(f11) ## 装修器:本质就是函数,功能是为其他函数添加附加功能 # 原则: # 1. 不修改被装饰函数的源代码 # 2. 不修改被修饰函数的调用方式 ## 装饰器 = 高阶函数 + 函数嵌套 + 闭包 def outer(func): def inner(): print('log') ret = func() print('after') return ret return inner @outer def f1(): print('F1') return '砍你哦.' @outer def f2(): print('F2') @outer def f100(): print('F100')
在另一个文件中调用上面定义的函数,看看装饰器是否启作用:
#!/usr/bin/env python # coding:utf-8 import outer01 outer01.f1() outer01.f2() outer01.f100()
装饰器简单原理:
#!/usr/bin/env python # coding:utf-8 def outer(func): def inner(): print('before') func() print('after') return inner ## 格式 @ + 函数名 ## 功能: # 1. 自动执行outer函数并且将其下面的函数名f1当作参数传递。 # 2. 将outer函数的返回值,重新赋值给f1 @outer def f1(): print('F1') # 一旦函数被装饰器装饰,函数就会被重新赋值成装饰器函数的内层函数 f1()
换一个文件调用函数:
#!/usr/bin/env python # coding:utf-8 import outer01 res = outer01.f1() # 也拿到了原函数的返回值。 print(res)
带有参数的装饰器:
#!/usr/bin/env python # coding:utf-8 import time def timmer(func): def wrapper(*args, **kwargs): start_time = time.clock() res = func(*args, **kwargs) stop_time = time.clock() print('运行时间是:%s' % (stop_time - start_time)) return res return wrapper @timmer def test(name, age): time.sleep(1) print('test执行完毕 %s %s' % (name, age)) return 'test complete.' def test2(name, age, gender): time.sleep(1.2) print('测试2 %s %d %s' % (name, age, gender)) res = test('alex', 22) print(res) test2('jerry', 88, 'male')
带参数的装饰器:
#!/usr/bin/env python # coding:utf-8 user_list=[ {'name':'alex','passwd':'123'}, {'name':'tom','passwd':'456'}, {'name':'jerry','passwd':'245'}, {'name':'kang','passwd':'135'}, ] current_dic = {'username':None,'login':True} def auth(auth_type='filedb'): def auth_func(func): def wapper(*args, **kwargs): print('认证类型是:',auth_type) if auth_type =='filedb': if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('请输入用户名:') passwd=input('请输入密码:') for user_dic in user_list: if username ==user_dic['name'] and passwd == user_dic['passwd']: current_dic['username'] = username current_dic['login'] = True res = func(*args, **kwargs) return res else: print('用户名或密码错误.') elif auth_type =='ldap': print('LDAP方式') res = func(*args, **kwargs) return res else: print('不知道什么方式.') res = func(*args, **kwargs) return res return wapper return auth_func @auth() def login(): print("欢迎登录。" ) @auth(auth_type='ldap') def shopping_cart(name): print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶')) @auth(auth_type='dsdsds') def home(name): print("%s 的主页" % name) login() shopping_cart('alex') home('alex')
小练习: 模拟网站增加登录状态验证:
#!/usr/bin/env python
# coding:utf-8
user_dic={'username':None,'login':False}
def auth_func(func):
def wapper(*args, **kwargs):
if user_dic['username'] and user_dic['login']:
res = func(*args, **kwargs)
return res
username=input('请输入用户名:')
passwd=input('请输入密码:')
if username =='user' and passwd == '123':
user_dic['username'] = username
user_dic['login'] = True
res = func(*args, **kwargs)
return res
else:
print('用户名或密码错误.')
return wapper
@auth_func
def login():
print("欢迎登录。" )
@auth_func
def shopping_cart(name):
print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶'))
@auth_func
def home(name):
print("%s 的主页" % name)
login()
shopping_cart('alex')
home('alex')
functools 模块中的 wraps
from functools import wraps def wraper(f): @wraps(f) # 此内置装饰器保证被装饰的函数的 __name__ 和 __doc__依然不变 def inner(*args,**kwargs): ''' 装饰器内部函数 inner ''' print('装饰在函数之前') ret = f(*args,**kwargs) print('装饰在函数之后') return ret return inner @wraper def holiday(day): ''' 这是一个放假函数 ''' print('公司决定放假{}天'.format(day)) return 'very happy.' # 获取函数名 和 函数文档 print(holiday.__name__) # 装饰器添加之后,结果变成了 inner print(holiday.__doc__) # 取函数的注释文档 ret = holiday(7) print(ret)
用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件
# 用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件 import time def recd(func): def inner(*args,**kwargs): with open('log','a',encoding='utf8') as f: f.write(time.strftime('%Y-%m-%d %X',time.localtime()) + ' ') f.write(func.__name__+'\n') ret = func(*args,**kwargs) return ret return inner @recd def shop_add(): print('增加了一件商品') @recd def shop_del(): print('删除了某个商品') # print(time.asctime()) # print(time.strftime('%Y-%m-%d %X',time.localtime())) shop_add() shop_del()
假设有2个装饰器,装饰同一个函数。
那么执行顺序是先执行第一个装饰器的前半部分,再执行第二个装饰器的前半部分,再执行被装饰的函数主体。
主体执行完成后,再执行第二个装饰器的后半部分,最后执行第一个装饰器的后半部分。
#多个装饰器装饰一个函数 def wrapper1(func): def inner1(): print('wrapper1 ,before func') ret = func() print('wrapper1 ,after func') return ret return inner1 def wrapper2(func): def inner2(): print('wrapper2 ,before func') ret = func() print('wrapper2 ,after func') return ret return inner2 def wrapper3(func): def inner3(): print('wrapper3 ,before func') ret = func() print('wrapper3 ,after func') return ret return inner3 @wrapper3 @wrapper2 @wrapper1 def f(): print('in f') return '哈哈哈' print(f())