• python-(5-2)函数的作用域、嵌套、闭包、装饰器、迭代器、生成器


    一. 作用域

    作用域:变量的访问权限

    函数的里面可以访问外面,外面不能直接访问里面。

    如果外面想返回里面,必须且只能通过return的返回值实现。

    def fun():
        x = 123
        return x
    val = fun()
    print(val)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    关键字:

    global:在局部,引入全局变量
    nonlocal:在局部,引入外层的局部变量

    二. 函数的嵌套

    2-1 函数的嵌套代码执行

    首先了解什么是函数的调用,以代码举例如下:

    def func1()
        pass
    
    def func2():
        func1()  # 调用函数func1
        
    func2()  # 调用函数func2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    函数的嵌套则是一层套着一层

    def func1():
        def func2():  # 函数的嵌套,局部变量
            pass
    
    • 1
    • 2
    • 3

    局部的东西,一般都是在局部自己访问使用的。

    下面构建一个嵌套的函数,理解其执行顺序。

    def func1():
        print(123)
        def func2():
            print(456)
            def func3():
                print(789)
            print(1)
            func3()
            print(2)
        print(3)
        func2()
        print(4)
    func1()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这13行代码的执行顺序分析如下,下面直接以数字表示(例如执行第3行代码就是“执行3”)

    首先调用函数,执行13,就会进入func1
    然后进入函数内部,执行2,最先输出执行结果:123
    然后按行执行3,定义函数func2,但此时并没有调用func2,因此忽略func2的内部函数体,执行10,输出打印的内容:3
    然后执行11,调用func2,因此进入func2中,即执行4,打印结果:456
    然后按顺序执行5,定义函数func3,由于没有调用,则执行7,打印结果:1
    然后按顺序执行8,调用函数func3,执行函数体,执行6,打印结果:789
    然后按顺序执行9,打印结果:2
    此时调用函数func2已经结束,继续按顺序执行12,打印结果4

    最终代码执行结果如下:

    123
    3
    456
    1
    789
    2
    4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此处的难点在于,定义函数def func()时,不需要执行函数体内部的代码,按照行的顺序向下依次执行即可;调用函数func()时,才会执行函数体内的代码,执行完后,退出当前函数,返回上一层函数。

    2-2 函数可以作为参数传递

    def func1():
        print("我是函数")
    
    def func2(fu):   # fu要求是一个函数
        fu()         # func1()
    
    func2(func1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2-3 函数可以作为返回值进行返回

    def func():
        def func_in():
            print("我是函数")
        return func_in
    
    ret = func()
    ret()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2-4 函数名实际上就是一个变量名,都表示一个内存地址

    def func1():
        print("func1")
    
    def func2():
        print("func2")
        
    func1 = func2
    
    func1()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三. 闭包

    本质是内层函数对外层函数局部变量的使用,此时内层函数被称为闭包函数

    它可以让一个变量常驻于内存,且避免全局变量被修改。

    以下面代码举例:

    def func():
        a = 10
        def func_in():
            nonlocal a
            a += 1
            return a
        return func_in
    
    ret = func()
    
    r1 = ret()
    print(r1)
    r2 = ret()
    print(r2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    四. 装饰器

    装饰器本质上是一个闭包。
    在不改变原有函数调用的情况下,给函数增加新的功能。
    简单来说,函数前后增加新功能,不改变源代码。

    def rap(fn):   # rap是装饰器,fn是目标函数
        def inner():     
            # 在目标函数执行之前
            fn() # 执行目标函数
            # 在目标函数执行之后
        return inner
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    五. 迭代器

    可迭代的数据类型都会提供一个叫迭代器的东西,它可以帮助我们把数据类型中的所有数据逐一拿到。

    爹迭代器本身也是可迭代的:
    只能向前不能反复;
    特别节省内存;
    惰性机制。

    获取迭代器的两个方法:
    1.使用iter()内置函数
    2.使用__iter__()特殊方法

    从迭代器中拿数据
    1.使用next()内置函数
    2.使用__next__()特殊方法

    举例代码说明:

    it = iter("北京欢迎你")
    
    print(next(it))
    print(next(it))
    print(next(it))
    print(next(it))
    print(next(it))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    或者:

    it = "北京欢迎你".__iter__()
    
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    for循环一定是拿迭代器的,所有不可迭代的东西不能用for循环
    迭代器统一了不同数据类型的遍历工作

    六. 生成器

    生成器本质是迭代器,生成器函数有一个关键字yield
    生成器函数执行时,并不会执行函数,得到的是生成器

    只要函数中出现了yield,它就是一个生成器函数
    作用:返回数据;分段执行函数中的的内容,通过__next__()执行到下一个yield的位置
    优势:用好了,特别节省内存

    def order():
        lst = []
        for i in range(1000):
            lst.append(f"衣服{i}")
            if len(lst) == 50:
                yield lst
                # 下一次拿数据
                lst = []
    
    gen = order()
    print(gen.__next__())
    print(gen.__next__())
    print(gen.__next__())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    如何使用VSCode来查看二进制文件
    [机缘参悟-38]:鬼谷子-第五飞箝篇 - 警示之一:有一种杀称为“捧杀”
    浏览器缓存
    科技云报道:都想当中国VMware,但用户体验拉平了吗?
    Java 设计模式实战系列—策略模式
    【Java 进阶篇】JavaScript 中的全局对象和变量
    Java入门7-面向对象基础
    链表高阶面试题及二叉树的遍历【前、中、后、层】
    C#开发的OpenRA游戏之生命值
    卓豪再签洛钼集团,实现AD域自动化管理有效降低管理人员工作负荷
  • 原文地址:https://blog.csdn.net/oldboy1999/article/details/126804163