016.Python之有参装饰器与迭代器

一、装饰器补充:有参装饰器

(一)什么是有参装饰器

在无参装饰器两层装饰的基础上,再增加一层函数,用于传递一个新的参数,这个装饰器就是一个有参装饰器。

(二)为何要用有参装饰器

无参装饰器实现了与原函数一样的调用方式,并通过闭包函数实现了原函数参数不被改变,当我们需要一个新的功能,而这个新的功能又需要一个参数的时候,无参装饰器就无法满足装饰器的原则,因此需要有参装饰器去实现这个功能。

# 无参装饰器  两层闭包无法给被装饰函数传递该函数本身需要参数之外的值
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
原文地址:https://www.cnblogs.com/huluhuluwa/p/13178780.html