• 6、python的高级特性(生成式、生成器、闭包、装饰器)


    生成式

    列表生成式

    在“先有一个空列表,然后通过循环依次将元素添加到列表中”的场景,可以使用列表生成式。

    列表生成式就是一个用来生成列表的特定语法形式的表达式。是python提供的一种生成列表的简洁形式,可以快速生成一个新的列表。

    思考如下需求:
    需求: 生成100个验证码(4个字母组成的验证码),
    平常做法:

    import string 
    import random
    codes=[]
    for i in range(100):
        code="".join(random.sample(string.ascii_letters, 4))
        codes.append(code)
    print(codes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    使用列表生成式
    for循环100次,每次生成的直接加入列表中

    import string
    import random
    codes = ["".join(random.sample(string.ascii_letters, 4)) for i in range(100)]
    print(codes)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    考虑需求:
    找出1-100之间可以被3整除的数。
    平常做法:

    nums=[]
    for i in range(1,101):
        if i %3==0:
            nums.append(i)
    print(nums)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用生成式实现:

    nums=[i for i in range(1,101) if i % 3 ==0]
    print(nums)
    
    • 1
    • 2

    在这里插入图片描述
    列表生成式中,for循环可以嵌套if语句,for循环也可以嵌套for循环,但是for循环嵌套for循环代码不太好读。

    字典生成式

    用来快速生成字典
    在“先有一个空字典,然后通过循环依次将元素添加到字典中”的场景,可以使用字典生成式。

    dic = {i: i ** 2 for i in range(10)}
    print(dic)
    
    • 1
    • 2

    在这里插入图片描述

    集合生成式

    用来快速生成集合

    在“先有一个空集合,然后通过循环依次将元素添加到集合中”的场景,可以使用集合生成式。

    result = {i ** 2 for i in range(10)}
    print(result)
    
    • 1
    • 2

    在这里插入图片描述

    生成器

    • 在Python中,一边循环一边计算的机制,称为生成器:Generator。
    • 什么时候需要用到生成器?
      性能限制需要用到 , 比如读取一个10G的文件,如果一次性将10G的文件加载到内存处理的话(read方法),内存可能会溢出;但使用生成器把读写交叉处理进行 ,比如使用(readline和readlines)就可以再循环读取的同时不断处理,这样就可以节省大量的内存空间.

    比如计算0到1000的数的平方:
    使用生成式,会稍微等待一会才能看到结果,而且结果是全部计算完成才返回。
    如果需要计算的数更大,那么等待时间会更长

    nums = [i ** 2 for i in range(1001)]
    print(nums) 
    
    • 1
    • 2

    生成器的实现方式

    将生成式改写成生成器。将[ ] 改成 ( )

    使用生成器:
    返回结果很快,而且返回的不是值,是一个generator。

    nums = (i ** 2 for i in range(1001))
    print(nums) 
    
    • 1
    • 2

    在这里插入图片描述生成器能一边循环一边计算,在需要的时候再进行计算
    如何打印出生成器的每一个元素呢

    • 通过for循环,依次计算并生成每一个元素
    • 如果要一个一个打印出来,可以通过 next( ) 函数获得生成器的下一个返回值
    nums = (i ** 2 for i in range(1001))
    print(next(nums))
    print(next(nums))
    print(next(nums))
    print(next(nums))
    
    for num in nums:
        print(num)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    使用yield关键字

    先需要说明,return关键字:
    函数遇到return就返回,return后面的代码不会执行

    def login():
        a=1
        return "login"
        print(a)
    
    result=login()
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    def login():
        print('step 1')
        yield 1
        print('step 2')
        yield 2
        print('step 3')
        yield 3
    
    result=login()
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当函数中有yield关键字,那么函数的返回值就是一个生成器
    在这里插入图片描述由于生成器是边循环边计算,因此可以使用 next( )打印出生成器的结果。

    函数遇到yield则会停止执行代码,当调用next方法时,会从上次停止的地方继续执行,遇到yield停止。。。

    def login():
        print('step 1')
        yield 1    #返回1
        print('step 2')
        yield 2
        print('step 3')
        yield 3
    
    result=login()    #result是一个生成器
    print(next(result)) #使用next()打印出生成器的第一个值
    print(next(result))  #使用next()打印出生成器的第二个值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    生成器的特点是什么?

    • 解耦. 爬虫与数据存储解耦;
    • 减少内存占用. 随时生产, 即时消费, 不用堆积在内存当中;
    • 可不终止调用. 写上循环, 即可循环接收数据, 对在循环之前定义的变量, 可重复使用;
    • 生成器的循环, 在 yield 处中断, 没那么占 cpu.

    闭包

    什么是时间戳:
    从1970年1月1日到现在经历的秒数

    # coding=gbk
    import time
    start_time=time.time() #时间戳
    time.sleep(2)
    end_time=time.time()
    print(f'休眠时间:{end_time-start_time}秒')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    闭包的特性:

    • 函数里面嵌套函数
    • 外部函数的返回值是内部函数的引用
    • 内部函数可以使用外部函数的变量
    # coding=gbk
    def timeit(name):
        def wrapper():
            print('wrapper '+name)
        print('timeit')
        return wrapper
    in_fun=timeit('lee')  #in_fun实质上就是wrapper函数
    in_fun()  #执行wrapper函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    装饰器

    装饰器是用来装饰函数的工具

    可以在不改变源代码的情况下,添加额外功能的工具。比如计算运行时间,记录日志,权限判断

    如何实现装饰器?依靠闭包实现

    下述程序,可以大致按照(1)(2)(3)(4)(5)(6)的顺序去执行

    # coding=gbk
    """
    需求:给计算两数之和的函数添加额外功能:计算该函数的运行时间
    """
    # 定义装饰器
    import time
    
    def timeit(f): #(2)
        def wrapper(x, y):  #(4)
            start = time.time()  # 被装饰函数add执行之前,计算时间
            result = f(x, y)  # 2.f(x,y)实质上是add加法函数
            end = time.time()  # 被装饰函数add执行之后,计算时间
            print("函数运行时间为: %.4f" % (end - start))
            return result
        return wrapper
    
    @timeit  # 1.语法糖  (1)
    # add=timeit(add) 装饰器要装饰add函数,将add函数作为参数传入,并将返回值wrapper赋给被装饰的函数add
    def add(x, y):   #(5)
        return x + y
    
    result = add(1, 3) #此时执行add函数,其实执行的是wrapper函数  (3)
    print(result)  #(6)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述
    如何自己写一个装饰器?

    #下述装饰器什么功能都没有,只是执行了被装饰函数
    def timit(f): #f是被装饰的函数
        def wrapper(*args,**kwargs):
            result=f(*args,**kwargs) #执行被装饰函数
            return result
        return  wrapper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    定义一个login()函数,执行该函数能打印login…
    现在希望给该函数添加功能,在打印login…之前,先显示一个欢迎界面
    那么就可以借助装饰器,装饰器就能在不改变源代码的基础上,给函数附加功能。类似地,如果有别的函数也希望添加一个欢迎界面,也能直接使用该装饰器。

    在wrapper函数内执行print(f._ _name_ _)能打印出被装饰函数的名称

    def timit(f): #f是被装饰的函数
        def wrapper(*args,**kwargs):
            print("welcome...")
            result=f(*args,**kwargs) #执行被装饰函数
            print(f"被装饰函数{f.__name__}执行完毕") 
            return result
        return  wrapper
    
    @timit
    def login():
        print("login...")
    
    login()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    def timit(f): #f是被装饰的函数
        """装饰器"""
        def wrapper(*args,**kwargs):
            """wrapper内置函数"""
            print("welcome...")
            result=f(*args,**kwargs) #执行被装饰函数
            print(f"被装饰函数{f.__name__}执行完毕")
            return result
        return  wrapper
    
    @timit
    def login():
        """login函数描述"""
        print("login...")
    
    print(help(login))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    print(help(login))查看login的帮助文档,但是显示的是wrapper函数的帮助文档。
    这是因为有装饰器,执行login函数的时候,其实执行的是wrapper。所以直接返回的是wrapper的帮助文档。怎么样才能实现访问login的说明文档就返回login的,不返回wrapper的?

    from functools import wraps
    def timit(f): #f是被装饰的函数
        """装饰器"""
        @wraps(f) #保留被装饰函数的属性信息和帮助文档!!!
        def wrapper(*args,**kwargs):
            """wrapper内置函数"""
            print("welcome...")
            result=f(*args,**kwargs) #执行被装饰函数
            print(f"被装饰函数{f.__name__}执行完毕")
            return result
        return  wrapper
    
    @timit
    def login():
        """login函数描述"""
        print("login...")
    
    print(help(login))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过 @wraps(f)装饰被装饰函数的属性信息,进而能保留被装饰函数的属性信息和帮助文档。
    在这里插入图片描述
    装饰器的实现模板:

    #装饰器的万能模板:
    from functools import wraps
    def 装饰器名称(f):
        @wraps(f)  # 保留被装饰函数的属性信息和帮助文档
        def wrapper(*args, **kwargs):
            # 执行函数之前做的事情
            result = f(*args, **kwargs)
            # 执行函数之后做的事情
            return  result
        return  wrapper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    练习-计算爬虫函数的运行时间

    # 需求:计算函数的运行时间
    import time
    from functools import wraps
    def timeit(f):
        @wraps(f)
        def wrapper(*args,**kwargs):
            start_time=time.time()
            result=f(*args,**kwargs)
            end_time=time.time()
            print(f'函数{f.__name__}运行时间为{end_time-start_time}秒')
            return result
        return wrapper
    
    @timeit
    def crawl():
        import requests
        url= 'https://logodownload.org/wp-content/uploads/2019/10/python-logo-0.png'
        content=requests.get(url).content #获取图片内容
        with open('doc/python.png','wb') as f: # wb,以二进制形式写入文件
            f.write(content)
            print('下载图片完成')
    
    crawl()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    带参数的装饰器

    import time
    def timeit(ti='seconds'):
        def desc(f):
            def wrapper(*args, **kwargs):
                start_time = time.time()
                result = f(*args, **kwargs)
                end_time = time.time()
                if ti=='seconds':
                    print(f'函数{f.__name__}运行时间为{end_time - start_time}秒')
                elif ti=='minutes':
                    print(f'函数{f.__name__}运行时间为{(end_time - start_time)/60}分钟')
                return result
            return wrapper
        return desc
    
    
    @timeit(ti='seconds')
    def crawl():
        import requests
        url= 'https://logodownload.org/wp-content/uploads/2019/10/python-logo-0.png'
        content=requests.get(url).content #获取图片内容
        with open('doc/python.png','wb') as f: # wb,以二进制形式写入文件
            f.write(content)
            print('下载图片完成')
    
    crawl()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    可以理解为先执行timeit()函数,该函数返回值为desc, @desc才是真正的装饰器,
    所以是将被装饰函数crawl传给desc,返回值再赋值为crawl, crawl=desc(crawl)
    在这里插入图片描述

    多个装饰器

    规则:执行装饰器的内容是从上到下,被装饰的顺序是从下到上

    # 多装饰器
    from functools import wraps
    def is_login(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            print(f"is_login装饰器判断是否登录")
            result = f(*args, **kwargs)
            print(f'is_login装饰器执行完成')
            return result
        return wrapper
    
    def is_permit(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            print(f"is_permit装饰器判断是否具有权限")
            result = f(*args, **kwargs)
            print(f'is_permit装饰器执行完成')
            return result
        return wrapper
    
    @is_login  #先运行哪个装饰器就把哪个装饰器写上面
    @is_permit
    def show_hosts():
        print("显示所有主机")
    
    show_hosts()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    先运行那个装饰器就把哪个装饰器写上面

    装饰过程:1、show_host=is_permit(show_hosts) 2、show_hosts=is_login(show_hosts)
    show_hosts=is_login(is_permit(show_hosts))

    在这里插入图片描述
    可以按照如下思路理解:

    将is_permit(show_hosts)看作是is_login () 的参数。先进入is_login() ,输出‘is_login装饰器判断是否登录’,然后执行到result = f(*args, **kwargs),也就是is_permit(show_hosts)函数,执行“print(f"is_permit装饰器判断是否具有权限")”,接着执行到is_permit的“result = f(*args, **kwargs)”函数,也就是show_hosts函数,输出“显示所有主机”。然后执行“print(f’is_permit装饰器执行完成’)”,最后执行“print(f’is_permit装饰器执行完成’)”。结束。

    高阶函数

    把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
    高阶函数:参数和返回值其中有一个为函数

    map()函数

    语法:map(function, iterable, …)

    map() 会根据提供的函数对指定序列做映射。
    map 函数是一个允许你使用另一个函数转换整个可迭代对象的函数。 这里的关键概念是转换,它可以包括但不限于:
    将字符串转换为数字
    四舍五入数字
    获取每个可迭代项的长度

    result = map(lambda x: x ** 2, [1, 2, 3, 4])
    print(list(result))
    
    result = map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6])
    print(list(result))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    注意点: map计算出的值,只能生效一次

    result = map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6])
    print(list(result)) #只有第一次生效,比较节省内存
    print(list(result))
    result = map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6])
    print(list(result)) 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    reduce() 函数

    reduce函数先从列表(或序列)中取出2个元素执行指定函数,并将输出结果与第3个元素传入函数,输出结果再与第4个元素传入函数,…,以此类推,直到列表每个元素都取完。

    from functools import reduce
    
    result = reduce(lambda x, y: x + y, [23, 4, 45, 6, 7])
    print(result)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    实现 123*…*100的结果

    from functools import reduce
    
    result = reduce(lambda x, y: x * y, range(1,101))
    print(result)
    
    • 1
    • 2
    • 3
    • 4

    filter() 函数

    # 筛选出列表中的偶数
    result = filter(lambda x: x % 2 == 0, [1, 23, 4, 68, 90])
    print(list(result))
    #筛选所有的奇数
    result = filter(lambda x: x % 2 != 0, [1, 23, 4, 68, 90])
    print(list(result))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    sorted() 函数

    #升序排序
    result = sorted([12,2,54,3])
    print(result)
    #降序排序
    result=sorted([12,2,54,3],reverse=True)
    print(result)
    #将所有的0排前面,非0值排后面
    result=sorted([23,7,1,0,90,0],key=lambda x: 0 if x==0 else 1)
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

  • 相关阅读:
    springboot
    基于SSM+MySQL+Boostrap简单的员工信息管理系统
    使用 ADO.NET 创建简单的数据应用程序
    什么是间谍软件恶意软件?
    Docker 部署常用应用(持续更新中)
    手机网络连接性能API接口:查询手机网络连接性能状态
    强大的Docker入门知识
    【C++】从文件获取json信息
    数据库系统原理与应用教程(067)—— MySQL 练习题:操作题 82-89(十一):数据的增、删、改操作
    算法通关村第十七关:青铜挑战-贪心其实很简单
  • 原文地址:https://blog.csdn.net/qq_43604376/article/details/128094686