• Python数据类型底层、推导、生成器、迭代器、装饰器



    系列文章:



    数据类型的底层实现

    1、列表的复制

    1.1、浅拷贝复制的奇怪现象

    对拷贝前后的两个列表进行操作

    1. list_2 进行操作
    list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
    # 浅拷贝
    list_2 = list_1.copy()
    list_2.append(55)
    print("list_1:  ", list_1) 
    #list_1:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}]
    
    print("list_2:  ", list_2) 
    #list_2:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 55]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. list_2[1] 进行操作
    list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
    # 浅拷贝
    list_2 = list_1.copy()
    list_2[1].append(55)
    print("list_1:  ", list_1) 
    #list_1:   [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
    
    print("list_2:  ", list_2)
    #llist_2:   [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.2、列表的底层实现

    1. 新增元素
    list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
    list_2 = list(list_1) # 浅拷贝  与list_1.copy()功能相同
    list_1.append(100)
    list_2.append("n")
    print("list_1:  ", list_1) 
    #list_1:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 100]
    
    print("list_2:  ", list_2) 
    #list_2:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 修改元素
    list_1[0] = 10
    list_2[0] = 20
    print("list_1:  ", list_1) 
    #list_1:   [10, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 100]
    
    print("list_2:  ", list_2) 
    #list_2:   [20, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 对列表型元素进行操作
    list_1[1].remove(44)
    list_2[1] += [55,66]
    print("list_1:  ", list_1) 
    # list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
    
    print("list_2:  ", list_2) 
    #list_2:   [20, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 'n']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结果表明:对列表型元素进行操作,两个表的列表型元素都同时改变

    1. 对元组型元素进行操作
    list_2[2] += (8,9)
    print("list_1:  ", list_1) 
    #list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
    
    print("list_2:  ", list_2) 
    #list_2:   [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah'}, 'n']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    元组是不可变的!
    对元组类型元素进行操作,两个表是独立的,互不影响
    5. 对字典型元素进行操作

    list_1[-2]["age"] = 18 
    print("list_1:  ", list_1) 
    #list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah', 'age': 18}, 100]
    
    print("list_2:  ", list_2) 
    #list_2:   [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah', 'age': 18}, 'n']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结论同列表

    1.3、引入深拷贝

    浅拷贝后:
    针对不可变元素(数字、字符串、元组)的操作,两个列表互不影响
    针对可变元素(列表、集合、字典)的操作,列表中的元素是有影响的

    引入深拷贝
    深拷贝将所有层级的相关元素全部复制,完全分开,渭泾分明,避免了上述问题

    import copy
    
    list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
    list_2 = copy.deepcopy(list_1)
    list_1[-1]["age"] = 18
    list_2[1].append(55)
    
    print("list_1:  ", list_1) #list_1:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah', 'age': 18}]
    print("list_2:  ", list_2) #list_2:   [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、字典

    2.1、快速的查找

    import time
    ls_1 = list(range(1000000))
    # [-10]*500 是初始化值为-10的长度为500的list, 之后使用+ 拼接两个list
    # 最终ls_2 长度是500 + 500
    ls_2 = list(range(500))+[-10]*500
    
    start = time.time()
    count = 0
    for n in  ls_2:
        if n in ls_1:
            count += 1
    end = time.time()
    print("查找{}个元素,在ls_1列表中的有{}个,共用时{}秒".format(len(ls_2), count, end-start))
    #查找1000个元素,在ls_1列表中的有500个,共用时3.092348575592041秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    import  time
    d = {i: i for i in range(1000000)}
    ls_2 = list(range(500))+[-10]*500
    
    start = time.time()
    count = 0
    for n in ls_2:
        try:
            d[n]
        except:
            pass
        else: count += 1
    end = time.time()
    print("查找{}个元素,在ls_1列表中的有{}个,共用时{}秒".format(len(ls_2), count,round(end-start)))
    # 查找1000个元素,在ls_1列表中的有500个,共用时0秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.2、字典的底层实现

    字典的创建过程

    1. 第一步:创建一个散列表(稀疏数组N >> n)
      d = {}
    2. 第二步:通过hash() 计算键的散列值
    print(hash("python"))
    print(hash(1024))
    print(hash((1,2)))
    -4771046564460599764
    1024
    3713081631934410656
    d["age"] = 18    # 增加键值对的操作,首先会计算键的散列值hash("age")
    print(hash("age")) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 第三步根据计算的散列值确定其在散列表中的位置
      极个别时候,散列值会发生冲突,则内部有相应的解决冲突的办法
    2. 在该位置上存入值

    键值对的访问过程

    d["age"]
    
    • 1
    1. 第一步:计算要访问的键的散列值
    2. 第二步:根据计算的散列值,通过一定的规则,确定其在散列表中的位置
    3. 第三步:读取该位置上存储的值
      如果存在,,则返回该值
      如果不存在,则报错 KeyError

    小结

    1. 字典数据类型,以空间换时间(空间利用率低),实现类快速地数据查找
    2. 因为散列值对应位置的顺序与键在字典中显示的顺序可能不同,因此表现出字典是无序的
      如果N=n 会产生很多位置冲突

    3、字符串

    通过紧凑数组实现字符串的存储

    • 数据在内存中是连续存放的,效率高
    • 同为序列类型,列表采用引用数组,而字符串采用紧凑数组

    3.1、可变类型与不可变类型

    不可变类型:数字、元组、字符串
    在生命周期中保持内容不变
    当改变了时,会有新的id,实际上创建了一个新的对象

    x= 1
    y = "Python"
    print("x id:", id(x)) #x id: 1447911844144
    print("y id:", id(y)) #y id: 1447917164912
    x += 2
    y += "3.7"
    print("x id:", id(x)) #x id: 1447911844208
    print("y id:", id(y)) #y id: 1447917208304
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    元组并不是总是不可变的

    t = (1,[2])
    print(id(t)) #2524431430976
    t[1].append(3) 
    print(id(t))#2524431430976
    print(t) #(1, [2, 3])
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可变类型:列表、字典、集合
    id 保持不变,但里面的内容可以变

    ls = [1, 2, 3]
    d = {"Name": "Sarah", "Age": 18}
    
    print("ls id:", id(ls)) #ls id: 2864434660288
    print("d id:", id(d)) #d id: 2864434589056
    ls += [4, 5]
    d_2 = {"Sex": "female"}
    d.update(d_2)    # 把d_2中的元素更新到d中
    print("ls id:", id(ls)) #ls id: 2864434660288
    print("d id:", id(d)) #d id: 2864434589056
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、列表操作的几个例子

    4.1、删除列表中特定的元素

    1. 法1 存在运算删除法
    alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
    s = "d"
    while True:
        if s in alist:
            alist.remove(s)
        else:
            break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 法2 一次性遍历元素执行删除
    alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
    s = "d"
    for i in alist:
        if i == s:
            alist.remove(s) # remove(s) 删除列表中第一次出现的该元素
    print(alist) #['2', '2', 'd', 'd', '4']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    解决方法:使用负向索引

    alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
    s = "d"
    for i in range(-len(alist),0):
        if alist[i] == s:
            alist.remove(alist[i])
    print(alist) #['2', '2', '4']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.2、多维列表的创建

    print([0]*10) #[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    print([[0]*10]*3)
    # [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    ls[0][0] = 1
    print(ls)
    # [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    #  [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    ls = [[0]*10 for i in range(3)]
    print(ls)
    # [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    ls[0][0]=1
    print(ls)
    # [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.4 列表推导

    解析语法的基本结构—以列表解析为例(也称为列表推导)

    [expression for value in iterable if conditihon]
    三要素:表达式、可迭代对象、if条件(可选)
    执行过程

    1. 从可迭代对象中拿出一个元素
    2. 通过if条件(如果有的话),对元素进行筛选。
      若通过筛选,则把元素传递给表达式
      若未通过,则进入步骤1,进入下一次迭代
    3. 将传递给表达式的元素,带入表达式进行处理,产生一个结构
    4. 将步骤3产生的结果作为列表的一个元素进行存储
    5. 重复步骤1-4直到迭代对象迭代结束,返回新创建的列表

    等价于如下过程:

    # 等价于如下代码
    result = []
    for value in iterale:
        if condition:
            result.append(expression)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    【例】求20以内奇数的平方

    squares = []
    for i in range(1,21):
        if i%2 == 1 :
            squares.append(i**2)
    print(squares) #[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]
    
    squares= [i**2 for i in range(1,21) if i%2==1]
    print(squares) #[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    支持多变量

    x = [1,2,3]
    y = [1,2,3]
    
    result = [i*j for i,j in zip(x,y)]
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    支持嵌套循环

    colors = ["black", "white"]
    sizes = ["S", "M", "L"]
    tshirts = ["{} {}".format(color, size) for color in colors for size in sizes]
    print(tshirts)
    #['black S', 'black M', 'black L', 'white S', 'white M', 'white L']
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.5 字典推导

    解析语法构造字典(字典推导)

    squares = {i: i**2 for i in range(5)}
    for k, v in squares.items():
        print(k, ":", v,end=",")#0 : 0,1 : 1,2 : 4,3 : 9,4 : 16,
    
    • 1
    • 2
    • 3

    4.6集合推导

    squares = {i**2 for i in range(5)}
    print(squares)#{0, 1, 4, 9, 16}
    
    • 1
    • 2

    4.7 生成器表达式

    squares = (i**2 for i in range(10))
    print(squares) # at 0x00000204790BF0B0>
    
    • 1
    • 2
    colors = ["black", "white"]
    sizes = ["S", "M", "L"]
    tshirts = ("{} {}".format(color , size) for color in colors for size in sizes)
    print(tshirts) # at 0x000002EF6451F0B0>
    for tshirt in tshirts:
        print(tshirt)
    # black S
    # black M
    # black L
    # white S
    # white M
    # white L
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.8条件表达式

    expr1 if condition else expr2
    【例】将变量n的绝对值赋值给变量x

    n = -10
    if n >= 0:
        x = n
    else: x = -n
    print(x) #10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    n = -10
    x = n if n >= 0 else  -n
    print(x) #10
    
    • 1
    • 2
    • 3

    条件表达式和解析语法简单实用、运行速度相对更快一些

    三大神器

    1、生成器

    ls = [i**2 for i in range(1,100001)]
    for i in ls:
        pass
    
    • 1
    • 2
    • 3

    缺点:占用大量内存

    生成器

    • List item采用惰性计算方式
    • 无需一次性存储海量数据
    • 一边执行一边计算,只计算每次需要的值
    • 实际上一直在执行next()操作,直到无值可取

    1.1生成器表达式

    • 海量数据,不需存储
    squares = (i**2 for i in range(100000))
    for i in squares:
        pass
    
    • 1
    • 2
    • 3
    • 求0~100的和

    无需显示存储全部数据,节省内存

    sum((i for i in range(101))) #5050
    
    • 1

    1.2 生成器函数—yield

    • 生产斐波那契数列
      数列前两个元素为1,1 之后的元素为其前两个元素之和
    def fib(max):
        n ,a, b = 0, 1, 1
        while n < max:
            print(a)
            a, b = b, a + b
            n += 1
    fib(5)
    1
    1
    2
    3
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构造生成器函数
    在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句继续执行

    def fib(max):
        n ,a, b = 0, 1, 1
        while n < max:
            yield a
            a, b = b, a + b
            n += 1
    print(fib(5)) #
    for i in  fib(5):
        print(i)
    1
    1
    2
    3
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.迭代器

    1、可迭代对象

    可迭代对象:可直接作用于for循环的对象统称为可迭代对象:Iterable
    (1) 列表、元组、字符串、字典、集合、文件

    from collections.abc import Iterable
    # Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    print(isinstance([1, 2, 3],Iterable)) #True
    print(isinstance((1, 2, 3),Iterable)) #True
    print(isinstance({1, 2, 3},Iterable)) #True
    print(isinstance("123",Iterable)) #True
    print(isinstance({1:1, "2":2},Iterable)) #True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (2) 生成器

    from collections.abc import  Iterable
    squares = (i**2 for i in range(5))
    print(isinstance(squares, Iterable)) #True
    
    • 1
    • 2
    • 3

    生成器不但可以用于for循环,还可以被next()函数调用

    from collections.abc import  Iterable
    squares = (i**2 for i in range(5))
    print(next(squares)) 
    print(next(squares))
    print(next(squares))
    print(next(squares))
    print(next(squares))
    print(next(squares))
    0
    1
    4
    9
    16
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    直到没有数据可取,抛出StopIteration

    print(next(squares))
    Traceback (most recent call last):
      File "E:\code\deeplea\pytorchlearn\ForTest\test.py", line 8, in <module>
        print(next(squares))
    StopIteration
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、迭代器

    可以被next()函数调用并不断返回下一个值,直到没有数据可取的对象称为迭代器: Iterator
    可以使用 isinstance() 判断一个对象是否是Iterator对象
    注意 可迭代对象:Iterable 用 isinstance([1, 2, 3],Iterable)判断
    迭代器:Iterator 用 isinstance([1, 2, 3], Iterator)

    (1)生成器都是迭代器

    from collections.abc import Iterator
    sequares = (i**2 for i in range(5))
    print(isinstance(sequares, Iterator)) #True
    
    • 1
    • 2
    • 3

    (2)列表、元组、字符串、字典、集合不是迭代器Iterator,但它们是可迭代对象Iterable

    from collections.abc import  Iterator
    
    print(isinstance([1, 2, 3], Iterator)) #False
    
    • 1
    • 2
    • 3

    可以通过 iter(Iterable) 创建迭代器

    print(isinstance(iter([1, 2, 3]),Iterator)) #True
    
    • 1

    for item in Iterable 等价于:
    先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration异常后循环结束。
    (3)zip enumetate 等itertools里的函数都是迭代器

    from collections.abc import Iterator
    
    x = [1, 2]
    y = ["a", "b"]
    print(zip(x ,y)) #
    for i in zip(x, y):
        print(i)
    print(isinstance(zip(x, y),Iterator)) #True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    from collections.abc import Iterator
    
    numbers = [1, 2]
    print(enumerate(numbers)) #
    for i in enumerate(numbers):
        print(i)
        # (0, 1)
        # (1, 2)
    print(isinstance(enumerate(numbers),Iterator)) #True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)文件是迭代器

    with open("测试文件.txt", "r", encoding = "utf-8") as f:
        print(isinstance(f, Iterator))#True
    
    • 1
    • 2

    (5)迭代器是可耗尽的

    squares = (i**2 for i in range(3))
    for square in squares:
        print(square)
    # 0
    # 1
    # 4
    
    for square in squares:
        print(square) #无输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (6)range() 不是迭代器

    from collections.abc import Iterator
    numbers  = range(10)
    print(isinstance(numbers,Iterator)) #False
    print(len(numbers)) ## 有长度
    print(numbers[0]) # 可索引
    print(9 in numbers) # 可存在计算
    next(numbers) # 不可被next()调用 TypeError: 'range' object is not an iterator
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    不会被耗尽

    for number in numbers:
        print(number) #有输出
        
    for number in numbers:
        print(number) #有输出
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以称range() 为懒序列,它是一种序列,但是并不包含任何内存中的内容,而是通过计算来回答问题

    3.装饰器

    1、需求的提出
    (1)需要对已开发上线的程序添加某些功能
    (2)不能对程序中函数的源代码进行修改
    (3)不能改变程序中函数的调用方式

    2、函数对象
    函数是Python中的第一类对象
    (1)可以把函数赋值给变量
    (2)对该变量进行调用,可实现原函数的功能

    def square(x):
        return x**2
    print(type(square)) #
    pow_2 = square #可以理解给这个函数起了个别名
    print(pow_2(5)) #25
    print(square(5)) #25
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以将函数作为参数进行传递

    3、高阶函数
    (1)接受函数作为参数
    (2)或者返回一个函数
    满足上述条件之一的函数称为高阶函数

    def square(x):
        return x**2
    
    def pow_2(fun):
        return  fun
    
    f = pow_2(square)
    print(f(8)) #64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    print(f == square) #True
    
    • 1

    4、嵌套函数
    在函数内部定义一个函数

    def outer():
        print("outer is running")
    
        def inner():
            print("inner is running")
    
        inner()
    
    outer()
    # outer is running
    # inner is running
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5、闭包

    def outer():
        x = 1
        z = 10
        
        def inner():
            y = x+100
            return y, z
            
        return inner
    
    
    f = outer()                # 实际上f包含了inner函数本身+outer函数的环境
    print(f)
    <function outer.<locals>.inner at 0x000001BE11B1D730>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    print(f.__closure__)         # __closure__属性中包含了来自外部函数的信息
    for i in f.__closure__:
        print(i.cell_contents)
    (<cell at 0x000001BE0FDE06D8: int object at 0x00007FF910D59340>, <cell at 0x000001BE0FDE0A98: int object at 0x00007FF910D59460>)
    1
    10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    res = f()
    print(res)
    (101, 10)
    
    • 1
    • 2
    • 3

    闭包:延伸了作用域的函数
    如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包,闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)

    • 一旦在内层函数重新定义了相同名字的变量,则变量称为局部变量
    def outer():
        x = 1
    
        def inner():
            # nonlocal  x
            x = x+100  #显示代码有误:不能解析x
            return x
    
        return inner
    
    f = outer
    f()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    def outer():
        x = 1
    
        def inner():
            nonlocal  x
            x = x+100
            return x
    
        return inner
    
    f = outer
    f()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    6、一个简单的装饰器
    嵌套函数实现

    import time
    def timer(func):
        def inner():
            print("inner run")
            start = time.time()
            func()
            end = time.time()
            print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
        return inner
    
    def f1():
        print("f1 run")
        time.sleep(1)
    
    # f1 包含inner() 和timer的环境,如传递过来的参数
    f1 = timer(f1) 
    f1()
    # inner run
    # f1 run
    # f1 函数运行用时1.01秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    语法糖

    import time
    def timer(func):
        def inner():
            print("inner run")
            start = time.time()
            func()
            end = time.time()
            print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
    
        return inner
    
    @timer #相当于实现类f1 = timer(f1)
    def f1():
        print("f1 run")
        time.sleep(1)
    
    f1()
    # inner run
    # f1 run
    # f1 函数运行用时1.01秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    7、装饰有参函数

    import time
    
    def timer(func):
        def inner(*args, **kwargs):
            print("inner run")
            start = time.time()
            func(*args, **kwargs)
            end = time.time()
            print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end - start)))
    
        return inner
    
    @timer
    def f1(n):
        print("f1 run")
        time.sleep(n)
    
    f1(2)
    # inner run
    # f1 run
    # f1 函数运行用时2.00秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    装饰函数有返回的情况

    import time
    
    def timer(func):
        
        def inner(*args, **kwargs):
            print("inner run")
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end - start)))
            return  res
    
        return inner
    
    @timer
    def f1(n):
        print("f1 run")
        time.sleep(n)
        return "wake up"
    
    res = f1(2)
    print(res)
    # inner run
    # f1 run
    # f1 函数运行用时2.00秒
    # wake up
    
    • 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

    8、带参数的装饰器
    装饰器本身要传递一些额外参数

    • 需求:有时需要统计绝对时间,有时需要统计绝对时间的2倍
  • 相关阅读:
    动态规划:得到目标货币的方法数(有限张货币 + 面值相同的货币相同)
    前端框架BootStrap
    11.27学术报告听讲笔记
    考研资讯平台|基于springboot框架+ Mysql+Java+Tomcat的考研资讯平台设计与实现(可运行源码+数据库+设计文档)
    (JAVA)P5705 【深基2.例7】数字反转
    为了拿捏后端打工人都要会的 Redis 数据结构,我画了 20 张图
    记一次使用nacos2踩到的坑
    网站接入微信支付后如何实现退款和取消预约?
    计算机网络
    vue中动态路由详解
  • 原文地址:https://blog.csdn.net/weixin_42382758/article/details/125866946