python装饰器(第一对象,函数名的应用,闭包)
第一对象
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
先来看一个简单的例子
def foo(text):
return len(text) ...
foo("zen of python")
这是一个再简单不过的函数,用于计算参数 text 的长度,调用函数就是函数名后面跟一个括号,再附带一个参数,返回值是一个整数。
函数身为一个对象,拥有对象模型的三个通用属性:id、类型、和值。
>>> id(foo) 4361313816 >>> type(foo) <class 'function'> >>> foo <function foo at 0x103f45e18>
作为对象,函数可以赋值给一个变量
>>> bar = foo
赋值给另外一个变量时,函数并不会被调用,仅仅是在函数对象上绑定一个新的名字而已。
>>> bar("zen of python") 13 >>>
同理,你还可以把该函数赋值给更多的变量,唯一变化的是该函数对象的引用计数不断地增加,本质上这些变量最终指向的都是同一个函数对象。
函数名的应用
def func():
print(666)
print(func)#直接打印函数名,得到的是函数的内存地址。<function func at 0x0000021A4F9612F0>
2.函数名可以赋值运算
def func():
print(666)
f=func
f()
3.函数名可以作为函数的参数
def func():
print(666)
def func1(x):
x()
print(999)
func1(func)
4.函数名可以作为容器类数据类型的元素
def func1():
print(111)
def func2():
print(222)
def func3():
print(333)
# l=[func1,func2,func3]
# for i in l:
# i()
dic={1:func1,2:func2,3:func3}#创建字典时,变量不用加引号''
for i in dic:
dic[i]()
5.函数名可以当函数的返回值
def func1():
print(333)
def func2(x):
print(666)
return x
ret=func2(func1)
ret()
闭包
内层函数对外层函数非全局变量的引用叫作闭包
判断是不是闭包__closure__
返回None不是闭包,返回call....是闭包
闭包有什么用
当执行一个函数时,如果解释器判断一个函数内部有闭包存在,python 就有一个机制,
此闭包所存在的临时命名空间不会随着函数的执行完毕而关闭
def func1():
name = '张山'
def inner():
print(name)
inner()
print(inner.__closure__) # <cell at 0x000000000282A768: str object at 0x0000000002856E10>
func1()
def func():
name='李四'
def inner():
nonlocal name
name='alex'
print(name)
inner()
print(inner.__closure__)#是闭包
print(name)
func()
def func():
print(999)
def inner(x):
print(666)
return x
ret=inner(func)
print(inner.__closure__)#None 不是闭包
ret()
def func1():
global name
name = '张三'
def inner():
print(name)
inner()
print(inner.__closure__) # None
func1()
def func1(x): #x='王五' 是赋值运算
def inner():
print(x) # 此时的王五是局部变量。
inner()
print(inner.__closure__)#(<cell at 0x00000211B5040B58: str object at 0x00000211B55C1440>,)
name = '王五'
func1(name)
def func(): #多看看
def func1():
name ="aa"
def func2():
nonlocal name
name = "bb"
def func3():
global name
name = "cc"
name = "dd"
func1()
print(name) # 1 dd
func2()
print(name) # 2,bb
func3()
print(name) # 3,bb
func()
print(name) # 4,cc
from urllib.request import urlopen
def index():
url = "http://www.xiaohua100.cn/index.html"
def get():
return urlopen(url).read()
return get
xiaohua = index()
content = xiaohua()
print(content)
装饰器
装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景
mport time
'''第一版本,测试函数low'''
def login():
time.sleep(0.3)
print('洗洗更健康...')
def timmer():
start_time = time.time()
login()
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
timmer()
改变了我原来执行函数的执行方式,不好
def login():
time.sleep(0.3)
print('洗洗更健康...')
# login()
def register():
time.sleep(0.4)
print('洗洗更健康22222...')
# register()
def timmer(f):
start_time = time.time()
f()
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
timmer(login)
timmer(register)
虽然执行函数的方式已经无限接近于原方式,但是更麻烦了,增加了两步代码。改
def login():
time.sleep(0.3)
print('洗洗更健康...')
# login()
def timmer(f):
start_time = time.time()
f()
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
f1 = login # 将login函数名给了f1
login = timmer # 将timmer函数名给了login
login(f1) # timmer(login)
初级装饰器
def login():
time.sleep(0.3)
print('洗洗更健康...')
# login()
def timmer(f): # f = login函数名
def inner():
start_time = time.time()
f() # login()
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
return inner
login = timmer(login) # inner 此login是新变量
login() # inner()
简单版装饰器 语法糖
def timmer(f): # f = login函数名
def inner():
start_time = time.time()
f() # login()
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
return inner
@timmer # login = timmer(login) # inner 此login是新变量
def login():
time.sleep(0.3)
print('洗洗更健康...')
login()
@timmer # register = timmer(register)
def register():
time.sleep(0.2)
print('洗洗更健康22...')
login() # inner()
被装饰的函数带参数的装饰器
def timmer(f): # f = login函数名
def inner(*args,**kwargs): # args (2, 3)
start_time = time.time()
f(*args,**kwargs) # login() *(2, 3) 2,3
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
return inner
@timmer # login = timmer(login) # inner 此login是新变量
def login(a,b):
print(a,b)
time.sleep(0.3)
print('洗洗更健康...')
login(2,3) # inner(2,3)
@timmer # register = timmer(register)
def register(a):
time.sleep(0.2)
print('洗洗更健康22...')
register(1) # inner(1)
def func1(x):
x = 0
print(x)
func1(0)
函数带返回值的装饰器 (万能装饰器)
import time
def timmer(f): # f = login函数名
def inner(*args,**kwargs): # args (2, 3)
start_time = time.time()
ret = f(*args,**kwargs) # login() *(2, 3) 2,3
end_time = time.time()
print('此函数的执行时间%s' % (end_time - start_time))
return ret
return inner
@timmer # login = timmer(login) # inner 此login是新变量
def login(a,b):
print(a,b)
time.sleep(0.3)
print('洗洗更健康...')
return 666
print(login(2,3)) # inner(2,3)
万能装饰器模板
def wrapper(f):
def inner(*args,**kwargs):
'''执行被装饰函数之前的操作'''
ret = f(*args,**kwargs)
"""执行被装饰函数之后的操作"""
return ret
return inner
装饰器功能:在不改变原函数的基础上,为原函数增加一些额外的功能,log,登录注册,等等.
在不改变原函数执行的情况下,为函数增加额外的功能。
import time
def timer(func):
def inner():
start = time.time()
func()#func=func1
print(time.time() - start)
return inner
@timer #==> func1 = timer(func1)
def func1():
print('in func1')
func1()
import time
def func1():
print('你有病啊,领导,测我的执行效率干甚?')
def timmer(f):#f=func1
def inner():
start_time = time.time()
f()#f=func1
time.sleep(0.3)
end_time=time.time()
print('此函数的执行效率%s' % (end_time-start_time))
return inner()
func1=timmer(func1)
func1()
import time
def timmer(func):
def inner(*args,**kwargs):
start_time=time.time()
ret=func(*args,**kwargs)
time.sleep(0.5)
end_time=time.time()
print('执行时间为%s' % (end_time-start_time))
return set
return inner
@timmer
# login=timmer(login)
def login():
print('hello,everyone,let''s go shopping')
login()
tast
1、整理装饰器的形成过程,背诵装饰器的固定格式
import time
def timmer(func):#这个形参随意设置
def inner(*args,**kwargs):
start_time=time.time()
time.sleep(0.3)
ret=func(*args,**kwargs)#和上面的func(形参)保持一致,相当于login
end_time=time.time()
print('此函数执行时间是%s' % (end_time-start_time))
return ret
return inner
@timmer #login=timmer(login)
def login(*args,**kwargs):
print(args)
print(kwargs)
func(10,11,name='alex')
万能带参数装饰器
def wrapper():
def inner(*args,**kwargs):
'''执行被装饰函数之前的操作'''
ret=func(*args,**kwargs):
'''执行被装饰函数之后的操作'''
retturn ret
return inner
2、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
'''
文件存储你的用户名,密码
'''
dic = {
'username':None,
'status':False,
}
def wrapper(func):
def inner(*args,**kwargs):
'''被装饰函数执行之前的操作'''
if dic['status']:#?
ret = func(*args, **kwargs)
return ret
else:
i = 1
while i < 4:
username = input('请输入你的账号:').strip()
password = input('请输入你的密码:').strip()
with open('register',encoding='utf-8')as f1:
for line in f1:
line=line.strip().split()
if username == line[0] and password == line[1]:
dic['username']=line[0]
dic['status']=True
print('恭喜你登录成功')
ret = func(*args, **kwargs)
return ret
else:
print('您输入的账号或者密码错误,还剩余%s次机会' % (3-i))
i += 1
return inner
@wrapper
def article():
print('欢迎登录文章页面')
@wrapper
def dariy():
print('欢迎登录日记页面')
@wrapper
def comment():
print('欢迎登录评论页面')
article()
dariy()
comment()
3、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
3.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
4.
写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组
例如:[(‘红心’,2), (‘草花’,2), …(‘黑桃’,‘A’)]
def func4(l1):
l2=[]
for i in l1:
for j in range(1,14):
l2.append((i,j))
l2[::13]= [('红桃', 'A'),('方片', 'A'),('黑桃', 'A'),('草花', 'A')]
l2[10::13]= [('红桃', 'J'),('方片', 'J'),('黑桃', 'J'),('草花', 'J')]
l2[11::13] = [('红桃', 'Q'),('方片', 'Q'),('黑桃', 'Q'),('草花', 'Q')]
l2[12::13] = [('红桃', 'K'),('方片', 'K'),('黑桃', 'K'), ('草花', 'K')]
return l2
l1=['红桃','方片','黑桃','草花']
print(func4(l1))
5.
写函数,传入n个数,返回字典
{‘max’:最大值,’min’:最小值}
例如: min_max(2, 5, 7, 8, 4)
返回: {‘max’:8,’min’:2}
def func5(l1):
max(l1)
min(l1)
return {'max':max(l1),'min':min(l1)}
l1=[1,2,3,4,5,6]
print(func5(l1))
def func5(*args):
max(args)
min(args)
print(args)#(1, 2, 3, 4, 5, 6)
print(*args)#1 2 3 4 5 6
return {'max':max(args),'min':min(args)}
print(func5(1,2,3,4,5,6))
def func5(*args): return {'max':max(args),'min':min(args)}
print(func5(1,5,6,789,456))
6.
写函数,专门计算图形的面积
其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
调用函数area(‘圆形’, 圆半径) 返回圆的面积
调用函数area(‘正方形’, 边长) 返回正方形的面积
调用函数area(‘长方形’, 长,宽) 返回长方形的面积
from math import pi
def area(*args):
if args[0]=='圆形':
def cricle():
cricle_area= pi*args[1]*args[1]
return cricle_area
return cricle()
elif args[0]=='正方形':
def aera_square():
aera_square=args[1]*args[1]
return aera_square
return aera_square()
else:
def area_rectangle():
area_rectangle=args[1]*args[2]
return area_rectangle
return area_rectangle()
print(area('圆形',2))
print(area('正方形',3))
print(area('长方形',3,6))
7.
写函数,传入一个参数n,返回n的阶乘
例如: cal(7)
计算7 * 6 * 5 * 4 * 3 * 2 * 1
def cal(n):
factorial=1
for i in range(n,1,-1):
factorial=factorial*i
return factorial
print(cal(8))
8、用面向函数的思想完成购物车(升级题)
函数一:实现三次登陆功能
函数二:实现新用户注册功能
函数三:购物功能
进阶任务:将购物功能拆分成多个函数
函数一:实现三次登陆功能
def login():
flag=True
while flag:
i=1
while i<4:
username=input('请输入你的账号:').strip()
password=input('请输入你的密码:').strip()
with open ('login',encoding='utf-8') as f1:
for line in f1:
line=line.strip().split()
if username==line[0] and password==line[1]:
print('恭喜你登录成功')
i=4
flag = False
else:
print('你输入的账号或者密码错误,还有%s次机会' % (3-i))
i+=1
login()
函数二:实现新用户注册功能
def register():
username=input('请输入你的账号:').strip()
password=input('请输入你的密码:').strip()
with open('register',encoding='utf-8') as f1:
for line in f1:
line=line.strip().split()
if username not in line[0]:
username = input('该账号不存在,请先注册账号:').strip()
password = input('请输入你的密码:').strip()
if username not in line[0]:
password1 = input('请再次输入你的密码:').strip()
if password1==password:
print('恭喜你注册成功')
with open('register',encoding='utf-8',mode='a') as f1:
f1.write( '\t' + username+'\t' + password)
# f1.write('\n'+'\t' + username + '\t' + password)#能执行,但是报错
else:
password1 = input('您两次输入的密码不一致,请再次输入你的密码:').strip()
else:
print('该账号已存在,请重新登录')
register()
函数三:购物功能
'''普通版购物车'''
goods = [{"name": "电脑", "price": 1999},
{"name": "鼠标", "price": 10},
{"name": "游艇", "price": 20},
{"name": "美女", "price": 998},
]
#
shopping_car = []
'''
shopping_car = {
0:{"name": "电脑", "price": 1999,'amount':0},
1:{"name": "鼠标", "price": 10,'amount':0}
}
'''
flag = True
while flag:
assets = input('请输入你的总资产:').strip()
if assets.isdigit():
assets = int(assets)
print('*****商品展示*****')
for num, goods_dic in enumerate(goods,1):
print("{}\t{}\t\t{}".format(num, goods_dic['name'], goods_dic['price']))
print('******************')
while True:
goods_num = input('请选择您要购买的商品序号/q或者Q结算:').strip()
if goods_num.isdigit():
goods_num = int(goods_num)
if 0 < goods_num <= len(goods):
if assets >= goods[goods_num - 1]['price']:
assets -= goods[goods_num - 1]['price']
print('您已经成功购买了%s商品' % goods[goods_num - 1]['name'])
shopping_car.append(goods[goods_num - 1]['name'])
else:
print('您的余额不足,还剩%d钱,请及时充值' % assets)
else:
print('您输入的序号超出范围,请重新输入')
elif goods_num.upper() == 'Q':
flag = False
print('您已成功购买如下商品:')
for i in shopping_car:
print(i)
break
else:
print('您输入的有非数字,请重新输入')
else:
print('您输入的有非数字,请重新输入')
l1 = ['aaa', 'bbb', 'ccc']
for i in enumerate(goods):
print(i)
for i in enumerate(l1,100):
print(i)
#升级版购物车
goods = [{"name": "电脑", "price": 1999},
{"name": "鼠标", "price": 10},
{"name": "游艇", "price": 20},
{"name": "美女", "price": 998},
]
shopping_car = {}
'''
shopping_car = {
0:{"name": "电脑", "price": 1999,'amount':0},
1:{"name": "鼠标", "price": 10,'amount':0}
}
'''
flag = True
while flag:
assets = input('\033[1;35;0m请输入你的总资产:\033[0m').strip()
if assets.isdigit():
assets = int(assets)
origal_assets = assets
print('*****商品展示*****')
for num, goods_dic in enumerate(goods,1):
print("{}\t{}\t\t{}".format(num, goods_dic['name'], goods_dic['price']))
print('******************')
while True:
goods_num = input('请选择您要购买的商品序号/q或者Q结算:').strip()
if goods_num.isdigit():
goods_num = int(goods_num)
if 0 < goods_num <= len(goods):
if assets >= goods[goods_num - 1]['price']:
assets -= goods[goods_num - 1]['price']
print('您已经成功购买了%s商品' % goods[goods_num - 1]['name'])
if (goods_num - 1) in shopping_car:
shopping_car[goods_num - 1]['amount'] += 1
else:
shopping_car[goods_num - 1] = {
"name": goods[goods_num - 1]['name'],
"price": goods[goods_num - 1]['price'],
'amount': 1,
}
else:
print('您的余额不足,还剩%d钱,请及时充值' % assets)
else:
print('您输入的序号超出范围,请重新输入')
elif goods_num.upper() == 'Q':
flag = False
print('您已成功购买如下商品:')
for i in shopping_car:
print('序号:{},商品名称:{},商品单价:{},购买数量:{}'.\
format(i+1,
shopping_car[i]['name'],
shopping_car[i]['price'],
shopping_car[i]['amount']))
print('此次购物消费%s元,还剩%s元' % (origal_assets-assets, assets))
break
else:
print('您输入的有非数字,请重新输入')
else:
print('您输入的有非数字,请重新输入')
print('\033[1;35;0m字体变色,但无背景色 \033[0m') # 有高亮 或者 print('\033[1;35m字体有色,但无背景色 \033[0m')
print('\033[1;45m 字体不变色,有背景色 \033[0m') # 有高亮
print('\033[1;35;46m 字体有色,且有背景色 \033[0m') # 有高亮
print('\033[0;35;46m 字体有色,且有背景色 \033[0m') # 无高亮
9,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。
这个作业之上进行升级操作:
设置两套密码,一套为微信账号密码,一套为qq账号密码保存在文件中。
设置四个函数,分别代表
京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
dic = {
'username':None,
'status':False,
}
def login(flag):
def wrapper(func):
def inner(*args, **kwargs):
if dic['status']:
ret = func(*args, **kwargs)
return ret
else:
i = 0
while i < 3:
username = input('请输入用户名(用%s账号):' % flag).strip()
password = input('请输入密码:').strip()
with open('user_pwd',encoding='utf-8') as f1:
msg_dic = eval(f1.readline())
# {'微信': {'password': '123', 'username': '老男孩'}, 'qq': {'password': '123', 'username': '老男孩1'}}
if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
dic['username'] = username
dic['status'] = True
ret = func(*args, **kwargs)
return ret
else:
print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))
i += 1
return inner
return wrapper
@login('微信')
def taobao_home():
print('淘宝首页')
@login('微信')
def taobao_shop():
print('淘宝超市')
@login('qq')
def jingdong_home():
print('京东首页')
@login('qq')
def jingdong_shop():
print('京东超市')
choice_dict = {
1: taobao_home,
2: taobao_shop,
3: jingdong_home,
4: jingdong_shop,
}
while True:
print('1 淘宝首页\n2 淘宝超市\n3 京东首页\n4 京东超市')
choice_num = input('请选择输入的序号:').strip()
if choice_num.isdigit():
choice_num = int(choice_num)
if 0 < choice_num <= len(choice_dict):
choice_dict[choice_num]()
else:
print('请输入范围内的序号')
else:
print('您输入的有非法字符,请重新输入')
10给每个函数写一个记录日志的功能,
功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
所需模块:
import time
def wrapper(func):
def inner(*args, **kwargs):
struct_time = time.localtime()
time_now = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
with open('log', encoding='utf-8', mode='a') as f1:
f1.write('在%s是时间,执行了%s函数\n' % (time_now, func.__name__))
ret = func(*args, **kwargs)
'''函数执行之后操作'''
return ret
return inner
@wrapper
def func1():
time.sleep(1)
print(555)
@wrapper
def func2():
time.sleep(2)
print(666)
func1()
func2()