一、装饰器(decorator)
- 装饰器本质上也是函数,目的是为其他函数添加附加功能(装饰其他函数)
- Python通过使用装饰器来达到代码的开放与封闭。
原则:
- 不能修改被装饰函数的源代码。
- 不能修改被装饰函数的调用方式。
我们现在有个需求:写一个计时器来统计每个函数执行的时间长度,满足上面的2个原则。
- 实现一:
import time
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print("The function run time is %s" % (stop_time-start_time))
return func
def f1():
print("This is function F1")
time.sleep(1)
timer(f1)
上述方法虽然可以实现需求,但是它却改变了原函数f1的调用方式,所以不满足第2个原则。
- 我们下面使用装饰器来实现:
import time
def timer(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print("The func run time is %s" % (stop_time-start_time))
return deco
@timer
def f1():
print("This is function F1")
time.sleep(1)
f1()
装饰器使用“@+函数名”的语法,当代码运行到@timer的时候,会把下面被装饰的函数f1做为自己的参数,相当于:func = f1,当f1()执行时,实际上执行的是deco()。
- 当被装饰的函数有参数时我们怎么实现呢?
- 参数固定时:
def login(func):
def deco(user):
print("[装饰器]正在验证登录:")
func(user)
return deco
@login
def wel(user):
print("Welcome %s to login the page!" % user)
wel("huyd")
2.参数不固定时:
def login(func):
def deco(*args,**kwargs):
print("[装饰器]正在验证登录:")
func(*args,**kwargs)
return deco
@login
def wel(*args,**kwargs):
print("Welcome {} to login the page!".format(*args,**kwargs))
wel("huyd")
- 当被装饰的函数有返回值时,又该怎么实现呢?
def login(func):
def deco(*args,**kwargs):
print("[装饰器]正在验证登录:")
res = func(*args,**kwargs)
return res # 此处应该将被装饰函数的返回值return
return deco
@login
def wel(*args,**kwargs):
print("Welcome {} to login the page!".format(*args,**kwargs))
return "sucessful" # 被装饰的函数有返回值
wel = wel("huyd")
print(wel)
- 装饰器也可以有自己的参数
def deco_fun1():
print("装饰器功能1")
def deco_fun2():
print("装饰器功能2")
def deco(deco_fun): # 第1层传入装饰器参数(这里只传入了1个参数,也可传入多个)
def outer(main_fun): # 第2层传入被装饰的函数名
def inner(): # 第3层传入被装饰函数的参数
deco_fun()
main_fun()
return inner
return outer
@deco(deco_fun1) # 这样就使用了装饰器deco功能1
def index():
print("我是主程序")
index()
需求:使用装饰器写一个简单的用户认证功能,当登录index页面时使用本地验证用户名,登录home页面时使用LDAP验证。
user,passwd = 'huyd','123'
def auth(auth_type): # 判断装饰器的参数
def outer_wrapper(func):
def wrapper(*args,**kwargs):
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if username == user and password == passwd:
print("\033[32;1mLogin successful!\033[0m")
res = func(*args,**kwargs)
return res
else:
print("\033[31;1mInvalid username or password!\033[0m")
elif auth_type == "ldap":
print("Home page must be LDAP type Login!")
return wrapper
return outer_wrapper
@auth(auth_type="local")
def index():
print("Welcome to index page!")
return "Index"
@auth(auth_type="ldap")
def home():
print("Welcome to home page!")
return "Home"
# index() # 进入index页面时使用本地验证登录
home() # 进入home页面时使用LDAP验证登录
二、生成器(generator)& 迭代器(iterator)
- 迭代器是访问集合中元素的一种方式,迭代器 object从集合中的第一个元素开始访问,知道所有的元素被访问完成.
- 所以迭代器的特点是:只能往前,不能后退
- 迭代器的优点:不需要提前准备整个迭代器中的所有元素,仅仅迭代到某个元素时才计算该元素,而之前或者之后,元素可以不存在或者销毁.因为这个特点,迭代器特别适合遍历文件比较大或者无限的集合.
- 总结下迭代器 iter()的特点吧:
- 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
- 不能随机访问集合中的某个值 ,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
迭代器是用__next__()来取值的,来看个例子吧:
gen = iter([1,2,3])
# 取值方法一:
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__()) #再取值就会报错
# 取值方法二:
for i in gen:
print(i)
能被for直接循环的都是可迭代对象,如列表,字典等。判断一个对象是不是可迭代对象,可以使用isinstance(*, Iterable)来判断。
生成器在被调用的时候返回一个迭代器。生成器有个标志性的语法yield。
当生成器被调用时,yield返回一个值,并记住当前位置,下一次调用时,从yield下一句语句运行返回下一个值。
def catch_mony(amount):
while amount > 0:
amount -= 100
print("又来取钱了!")
yield "给你100吧,剩余:{}".format(amount)
atm = catch_mony(500)
print(type(atm))
print(atm.__next__())
print(atm.__next__())
print("去看电影!。。。")
print(atm.__next__())
======================================
运行结果:
<class 'generator'>
又来取钱了!
给你100吧,剩余:400
又来取钱了!
给你100吧,剩余:300
去看电影!。。。
又来取钱了!
给你100吧,剩余:200
此外,yield还有实现在单线程模式下实现并发运算的效果。如下例:
import time
def consumer(name):
print("{}准备吃包子啦!".format(name))
while True:
baozi = yield # yield也可以用于接收值
print("第{}次的包子来了,被{}吃掉了!".format(baozi, name))
def producer(name):
c1 = consumer('A')
c2 = consumer('B')
c1.__next__()
c2.__next__()
print("{}准备开始做包子了!".format(name))
for i in range(1, 11):
time.sleep(1)
print("{}第{}次做了两个包子。".format(name, i))
c1.send(i) # 将i的值传给yield
c2.send(i) # 将i的值传给yield
producer("老板")
三、Json & pickle 数据序列化
用于序列化的两个模块:
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
- Json模块提供了四个功能:dumps、dump、loads、load
- pickle模块提供了四个功能:dumps、dump、loads、load
import json
data = {'k1':123,'k2':'Hello'}
# json.dumps 将数据通过特殊的形式转换为所有程序语言都认识的字符串
j_str = json.dumps(data)
print(type(j_str),j_str)
# json.loads 将字典形式的字符串转换为字典格式
j_dict = json.loads(j_str)
print(type(j_dict),j_dict)
# json.dump 将数据通过特殊的形式转换为所有程序都认识的字符串,并写入文件
with open('result.json','w') as f:
json.dump(data,f)
#######################################################################
import pickle
# pickle.dumps 将数据通过特殊的形式转换为只有python语言认识的字符串
p_str = pickle.dumps(data)
print(type(p_str),p_str)
# pickle.dump 将数据通过特殊的形式转换为只有Python语言认识的字符串,并写入文件
with open('result.pk','w') as f:
pickle.dump(data,f)