详解Python迭代器,生成器,装饰器

迭代器
  • 简介:迭代器是python里面可以记住遍历位置的对象,迭代器只能往前不能往后,使用iter()创建一个迭代器,使用next()返回一个迭代器里面的元素。
  • 应用场景:数列的数据规模巨大,或者数列有规律,但是通过列表推导式推导不出来
#!/usr/local/bin/python3
  import sys
  it = iter([1,32,43,2])
  while True:
           try:
                  print(next(it))
           except StopIteration:
                   sys.exit()

参考:
廖雪峰的迭代器教程
菜鸟教程:迭代器与生成器
Tyson Lee的博客:详解高阶函数,闭包,装饰器

迭代器协议

迭代器协议是使用__next__()__iter__()实现的

class Foo:
    def __init__(self,start,stop):
        self.num=start
        self.stop=stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.num >= self.stop:
            raise StopIteration
        n=self.num
        self.num+=1
        return n
迭代器协议实现斐波那契数列

在这里插入图片描述

生成器
  • 简介:在python里面使用了yield的函数称为生成器,生成器返回一个迭代器,在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象

应用场景:

  • 列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。
  • 如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
  • 在Python中,这种一边循环一边计算的机制,称为生成器:generator
  • generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
  • 简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器

例子:
最简单的创建生成器的例子

>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
#!/usr/bin/python3
 
import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

参考: 廖雪峰的生成器教程

装饰器

参考:
B站的视频教程
廖雪峰的装饰器教程
菜鸟教程装饰器
学装饰器前需要理解以下知识:

大家不要觉得这里啰嗦哈,认真看肯定有收获

  • 函数也是变量,我们那些普通的变量,比如定义x=1的时候,我们1会实实在在地存在内存里面,然后把x指向1,当在赋值x=y的时候,就把y也指向1了。当我们使用del去删除x和y的时候,那么就没有变量指向1这个内存地址了,这个1就会被Python解释器回收掉,那么如果没有被删的话,它会不会被回收呢,答案是不会,除非程序运行结束了,如果没有被删的话,它会一直贮存在内存里面,不会被回收,只要这个x变量存在,它就永远不会被删除,因为x已经用了这个内存了,就是说1是有人用了的,也就是其实他内存里面有个计数器,它每隔多长时间,就(相当于)刷新一下,看哪些没有被引用就把它清除,直到程序结束就都清空了,我们del的时候是吧x这个引用摘掉了,但那个1还在内存里面,然后Python垃圾回收发现这个1哎它没有引用,就把它删掉。
  • 装饰器对被装饰函数是完全透明的,也就是说,装饰器不会对要加功能的函数的源代码进行修改,而且也不会改变函数的调用方式。
  • 装饰器其实就是使用了嵌套函数和高阶函数的知识,可以看本博的装饰器进阶的部分,都嵌套两层了

简介:装饰器本质上就是一个函数,它用来给别的函数增加功能。装饰器可以在代码运行期间动态地增加函数的功能,当我们想要给多个函数增加相同的功能,一个一个地区修改这些函数效率很低,而且代码混乱,我们可以采用装饰器
看一个例子:

#!/usr/local/bin/python3
import time
def display_time(func):
        def wrapper():
                t1 = time.time()
                func()
                t2 = time.time()
                print(t2-t1)
        return wrapper
def is_Prime(num):
        if num < 2:
                return True
        elif num ==  2:  
                return False
        else:
                for i in (2,num):
                        if num % i == 0:
                                return False
                return True
@display_time  
# 这里的原理是find_Prime=display_time(find_Prime)
# 相当于偷梁换柱了,我们后面再调用的时候实际上调用的是display_time
def find_Prime():
        for i in range(2,10000):
                if is_Prime(i):
                        print(i)
find_Prime()

但是如果我们的调用装饰器的函数里面要返回一个值的话,可以这样写,修改装饰器代码的第六行和加上第九行

#!/usr/local/bin/python3
import time
def display_time(func):
        def wrapper():
                t1 = time.time()
                result = func()
                t2 = time.time()
                print(t2-t1)
                return result
        return wrapper
def is_Prime(num):
        if num < 2:
                return True
        elif num ==  2:  
                return False
        else:
                for i in (2,num):
                        if num % i == 0:
                                return False
                return True
@display_time
def find_Prime():
        count = 0 
        for i in range(2,10000):
                if is_Prime(i):
                        count += 1
        return count

再进一步,如果我们要往装饰器里面传参数,比如我不一定是计算2到10000中的素数,我要计算2到n里面的素数,可以这样改写装饰器,在wrapper()改成wrapper(n),func()改成func(n)就行

def display_time(func):
       def wrapper(n):
               t1 = time.time()
               result = func(n)
               t2 = time.time()
               print(t2-t1)
               return result
       return wrapper
def is_Prime(num):
       if num < 2:
               return True
       elif num ==  2:  
               return False
       else:
               for i in (2,num):
                       if num % i == 0:
                               return False
               return True
@display_time
def find_Prime(n):
       count = 0 
       for i in range(2,n):
               if is_Prime(i):
                       count += 1
       return count
find_Prime(10000)

但是以上代码写死了,就只能传一个参数,如果我们想要被装饰器的函数可以带0个或多少个参数都可以,怎么办。此时我们可以吧def wrapper(n)改成def wrapper(*args,**kwargs)

装饰器进阶骚操作

这上面的装饰器代码都只实现了增加函数的功能,以及接受参数,以及可以返回函数的结果,但是还没有实现进阶骚操作。
需求:我想编写一个登录跳转的程序,比如通过密码,用户名进行从首页登录到其他页面,这时做运维的小伙伴知道,我们登录可以账号密码登录,又可以ldap的方式登录,那么我想让用户两种方式都登录,如果编写两个装饰器,那就low到爆,这时候我们可以给他再嵌一套。

import time
user,passwd = 'alex','abc123'
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)  # from home
                    print("---after authenticaion ")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
            elif auth_type == "ldap":
                print("搞毛线ldap,不会。。。。")

        return wrapper
    return outer_wrapper

def index():
    print("welcome to index page")
@auth(auth_type="local") 
# home = wrapper(),加了括号,已经调用了wrapper()
def home():
    print("welcome to home  page")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs  page")

index()
print(home()) #wrapper()
bbs()

参考:alex运维开发的博客

  • 26
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值