目录
一、装饰器补充:有参装饰器
(一)什么是有参装饰器
在无参装饰器两层装饰的基础上,再增加一层函数,用于传递一个新的参数,这个装饰器就是一个有参装饰器。
(二)为何要用有参装饰器
无参装饰器实现了与原函数一样的调用方式,并通过闭包函数实现了原函数参数不被改变,当我们需要一个新的功能,而这个新的功能又需要一个参数的时候,无参装饰器就无法满足装饰器的原则,因此需要有参装饰器去实现这个功能。
# 无参装饰器 两层闭包无法给被装饰函数传递该函数本身需要参数之外的值
import time
def outter(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
# @函数的内存地址1(1,2,3,4,5) # @的作用是给函数的内存地址加括号,调用函数index
def index(x, y):
print('index===>', x, y)
@outter
def home(name):
print('home====>', name)
# 引出两个点:
# 1、可以通过闭包的方式为函数体传参,可以包一层,也可以包两层
# 2、@后跟的必须是一个函数的内存地址,@函数的内存地址(1,2,3) 是可以的,但是前提是调用函数"函数的内存地址(1,2,3)"的返回值必须是一个函数的内存地址
(三)有参装饰器如何使用
通过示例,演示有参装饰器的实现。
1.示范一
# 示范一: 无参装饰器
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("
").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
return wrapper
@outter
def index(x, y):
print('index===>', x, y)
index(1, 2)
2.示范二
# 示范二:增加用户验证数据的多个来源
# ldap
# mysql
# file
def outter2(mode):
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
if mode == "file":
print("认证来源=====》file")
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("
").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
elif mode == "mysql":
print("认证来源=====》mysql")
elif mode == "ldap":
print("认证来源=====》ldap")
else:
print("未知认证来源")
return wrapper
return outter
outter = outter2(mode="mysql")
@outter #语法糖@触发加括号运行 outter(index)=index ====》index = wrapper
def index(x, y):
print('index===>', x, y)
index(1, 2)
3.示范三
# 示范三:语法糖加载方式简化
def outter2(mode):
def outter(func):
def wrapper(*args, **kwargs):
inp_name = input("please input your username: ").strip()
inp_pwd = input("please input your password: ").strip()
if mode == "file":
print("认证来源=====》file")
with open("db.txt",mode="rt",encoding="utf-8") as f:
for line in f:
name_db, pwd_db, balance_db = line.strip("
").split(":")
if inp_name == name_db and inp_pwd == pwd_db:
print("login successful")
res = func(*args, **kwargs)
return res
else:
print("login failed")
elif mode == "mysql":
print("认证来源=====》mysql")
elif mode == "ldap":
print("认证来源=====》ldap")
else:
print("未知认证来源")
return wrapper
return outter
@outter2(mode="mysql") # outter2(mode="mysql")就是返回值outter,语法糖@触发加括号运行 outter(index)=index ====》index = wrapper
def index(x, y):
print('index===>', x, y)
index(1, 2) # wrapper(1, 2)
二、迭代器
(一)什么是迭代器
迭代是一个重复的过程,每一次重复都是基于上一次的结果而来的,但迭代不是单纯的重复。
而迭代器,是一种迭代取值的工具,这种取值方式是通用的,不依赖于索引。
(二)为什么要有迭代器
迭代器是用来迭代取值的工具,而涉及到把多个值循环取出来的类型有:列表(索引取值)、字符串(索引取值)、元组(索引取值)、字典(key取值)、集合(既没key也没索引)、f文件对象(既没key也没索引)等。
t = (111, 222, 333, 444, 555, 666)
i = 0
while i < len(t):
print(t[i])
i += 1
上述使用while循环迭代取值的方式只适用于有索引的数据类型:列表、字符串、元组,为了解决基于索引迭代器取值的局限性,python提供了一种能够不依赖于索引的取值方式,这就是迭代器。
(三)如何使用迭代器
1.可迭代对象(iterable)
从语法形式上讲,内置有 ._ _ iter _ _ ()方法的对象都是可迭代对象,字符串、列表、元组、字典、集合、打开的文件对象f都是可迭代对象,
s = "hello"
s.__iter__()
l = [111, 222, 333]
l.__iter__()
t = (1111, 222, 333, 444, 555, 666)
t.__iter__()
d = {"k1": 111, "k2": 222, "k3": 3333}
d.__iter__()
s1 = {'a', 'b', 'c'}
s1.__iter__()
f = open(r'db.txt', mode='rt', encoding='utf-8')
f.__iter__()
f.close() # 文件关闭后则无法调用,会报错
2.迭代器对象
调用 ._ _ iter _ _()返回的是迭代器对象。
d = {"k1": 111, "k2": 222, "k3": 3333}
res = d.__iter__() # res=iter(d)
print(res) # res是迭代器 <dict_keyiterator object at 0x00000240CD17B860>
a = res.__next__() # a=next(res)
b = res.__next__() # b=next(res)
c = res.__next__() # c=next(res)
d = res.__next__() # StopIteration
# 此时,引入try-except 语句,进行异常监控, 提供处理异常的机制
d = {"k1": 111, "k2": 222, "k3": 3333}
iter_d = iter(d) # 制造一个迭代器
while True:
try:
print(next(iter_d))
except StopIteration:
break # 此时,可以取出字典内所有的key值
# 在一个迭代器取值取干净的情况下,再对其取值取不到,除非再次制造一个迭代器
iter_d = iter(d) # 再次制造一个迭代器
while True:
try:
print(next(iter_d))
except StopIteration:
break
文本文件可循环的原理:
f = open(r'db.txt', mode='rt', encoding='utf-8')
line=f.__next__()
print(line) # aaa:123:11
line=f.__next__()
print(line) # bbb:123:222
for line in f:
print(line)
f.close()
line=f.__next__() # 报错 ValueError: I/O operation on closed file.
3.可迭代对象与迭代器对象详解
# 可迭代的对象:有__iter__内置方法的对象都是可迭代的对象,str、list、tuple、dict、set、文件对象
# ps:可迭代对象.__iter__()返回的是迭代器对象
# 迭代器对象:
# 1、有__next__方法
# 2、有__iter__方法,调用迭代器的__iter__方法得到的就是迭代器自己
# ps:迭代器对象之所内置__iter__方法是为了符合for循环第一个工作步骤
d = {"k1": 111, "k2": 222, "k3": 3333}
res=d.__iter__()
print(res) # <dict_keyiterator object at 0x0000022AFD9E4770>
print(res.__iter__()) # <dict_keyiterator object at 0x0000022AFD9E4770>
print(res.__iter__() is res) # True 调用迭代器的__iter__方法得到的就是迭代器自己
print(res.__iter__().__iter__().__iter__() is res) # True
(四)for循环的工作原理
for循环可以称之为叫迭代器循环
d = {"k1": 111, "k2": 222, "k3": 3333}
for k in d:
print(k)
for循环的工作步骤
1、调用in后的对象的__iter__方法,得到对应的迭代器
2、k=next(迭代器),然后执行一次循环
3、循环往复,知道把迭代器的值取干净了,抛出异常,for循环会自动捕捉异常,结束循环
(五)迭代器的优缺点
1.优点:
(1)不依赖索引,是一种通用的取值方式
(2)节省内存
d = {"k1": 111, "k2": 222, "k3": 3333}
iter_d=iter(d)
next(iter_d)
2.缺点:
(1)不能取指定位置的值
(2)不能预估值的个数,无法统计长度
ll = [111, 222, 333]
print(ll[2]) # 333
iter_ll=iter(ll)
next(iter_ll)
next(iter_ll)
print(next(iter_ll)) # 333