• day11-函数对象,函数嵌套,名称空间与作用域,闭包函数,以及装饰器的前言


    一.函数对象

    在python中:函数是第一类对象,函数是第一等公民
    本质:函数可以当作变量用

    1.可以赋值

    def func():
        print('from func')
    
    
    f = func
    print(f)  # <function func at 0x0000017E65C12288>
    f()  # from fun
    

    2.可以当作函数传给另外一个函数

    def func():
        print('from func')
        
    def foo(x):
        print(x)  # <function func at 0x000002D284BB7EE8>
        x()  # from func
    
    foo(func)
    

    3.可以当作函数的返回值

    def func():
        print('from func')
    
    def foo(x):
        return x
    
    
    res = foo(func)
    print(res)  # <function func at 0x000001E64BDAC048>
    

    4.可以当作容器类型的元素

    def func():
        print('from func')
    
    l = [func, ]
    print(l)  # [<function func at 0x000001E64BDAC048>]
    l[0]()  # from func
    

    函数名当作容器类型的元素之字典的存放 案例:

    def withdraw():
        print('取款')
    
    
    def transfer():
        print('转账')
    
    
    def check_balance():
        print('查询余额')
    
    
    def deposit():
        print('存款')
        
    
    func_dic = {
        "0": ['退出', exit],
        "1": ["提款", withdraw],
        "2": ["转账", transfer],
        "3": ["查询余额", check_balance],
        "4": ["存款", deposit]
    }
    
    while True:
        for k, v in func_dic.items():
            print(k, v[0])
    
        choice = input(">>>: ").strip()
        if choice in func_dic:
            func_dic[choice][1]()
        else:
            print("输入错误")    
    

    函数名当作容器类型的元素之列表的存放 案例:

    def withdraw():
        print('取款')
    
    
    def transfer():
        print('转账')
    
    
    def check_balance():
        print('查询余额')
    
    
    def deposit():
        print('存款')
        
      
    func_list = [["退出", exit], ["取款", withdraw], ["转账", transfer], ["查询余额", check_balance], ["存款", deposit]]
    
    while True:
        for index1, func1 in enumerate(func_list):
            print(f"{index1} 的功能为{func1[0]}")
        chios = input(">>>>:").strip()
        if not chios.isdigit(): continue
        chios = int(chios)
        for index, func in enumerate(func_list):
            if chios == index:
                func_list[chios][1]()
        else:
            print('暂不支持')
    
    

    二.函数的嵌套

    函数的嵌套定义: 在一个函数中定义了另外一个函数

    例子:比较4个数的大小

    def max2(a,b):
        if a > b:
            return a
        else:
            return b
    
    
    def max4(a,b,c,d):
        res = max2(a, b)
        res1 = max2(res,c)
        res2 = max2(res1,d)
        return res2
    
    print(max4(15,523,523,535))
    

    例子:函数内的变量名在外部不能使用

    def f1():
        x = 111
        def f2():
            print('from f2')
        print(x)
        print(f2)
    
    f1()
    # print(x)  # NameError: name 'x' is not defined
    # print(f2)  # NameError: name 'f2' is not defined
    
    # 总结,在函数体内部定义的"变量"只能在自己的函数体内部,被引用,在函数体外则不能被引用
    

    三.名称空间与作用域

    名称空间

    概念:namespaces名称空间,存放名字的地方,具体来说是名字和内存地址绑定关系的地方

    内置名称空间: 存放内置的名字

    ​ 生命周期:python解释器启动则删除,关闭则销毁

    全局名称空间:除了在函数内的名称的名字

    ​ 生命周期:运行python文件时则产生,python文件运行完毕则销毁

    • x = 1
      if 1 > 0:
          x1 = 2
      while True:
          x2 = 3
      以上的xn都是全局名称 也都是顶级的
      

    局部名称空间:存放的是函数内的名字

    ​ 生命周期:执行函数内的代码则产生,执行完就销毁

    核心:名字的访问优先级

    ​ 基于当前所在的位置向外查找
    ​ 函数内---> 外层函数 ---> 全局 ---> 内置

    案例1:len不同的名称空间的使用

    len = 10
    
    def func():
        len = 20
        print(len)
    
    func()  # 20
    print(len)  # 10
    

    案例2:执行f1函数,x的值会怎么变化(将函数体代码x的注释打开,从里到外)

    def f1():
        # x = 555
        def f2():
            # x = 666
            print(x)
    
        f2()
    
    
    x = 444
    f1()  # 在执行f1之前全局名称空间中就有x这个名称了
    
    f1()  # 在执行f1的时候,名称空间中还没有x这个名称,报错:NameError: name 'x' is not defined
    x = 444
    

    专业术语:LEGB : L ---> 内层函数; E-----> 外部函数; G-----> 全局名称,B---->内置名称

    名称空间与作用域的关系是在函数定义阶段(扫描语法)就确立的,与什么时候调用以及调用位置无关

    案例1:判断下列函数中x的值打印出来是什么?

    x = 111
    def f1():
        print(x)  # x只管在哪被定义的
        x = 222
    
    f1()
    

    案例2:当在局部名称空间中改变全局名称空间的可变数据类型时

    l = []
    def func():
        l.append(1111)  # l表示的是全局名称空间的l
    
    func()
    print(l)  # 打印出的全局名称空间的l为[1111]
    

    上面在函数内即局部名称空间改变全局名称空间用的是.append功能,改变的是全局的,但你在局部给他重新赋值看看

    l = []
    def func():
        l = [11,22,33,44]
    
    func()
    print(l)  # []
    

    你会发现并没有 给 全局的l重新附上 对应的 值,原因就是 局部的l=[11,22,33,44]是一个新的l,即是局部的l.

    现在有一个需求.就是将全局名称空间的值是一个不可变的类型,我能怎么更改呢???

    你可能会试着用 x = xxx 给他重新赋值 或者 字符串的操作,但是你会发现它都不能更改,因为是一个不可变的数据类型,所以python给我们提供了一个global关键字,表示其后面的名称是全局的

    案例:将x这个整数111改为222

    x = 111
    
    def func():
        global x  # 声明这里用的x是一个全局变量
        x = 222
    
    func()
    print(x)  # 222
    

    而对应的也有一个关键字可以声明变量名是来自于外层函数的,不是全局的,这个关键字就是nonlocal

    案例:

    # nonlocal  # 声明变量名是来自与外层函数的,不是全局
    x = 111
    def f1():
        x = 222
        def f2():
            # global x # 声明变量名是来自于全局
            nonlocal x  # 声明变量名是来自于外层函数的,不是全局
            x = 333
        f2()
        print(x)
        
    f1()  # 333
    

    四.闭包函数=函数对象+函数嵌套+名称空间与作用域

    闭包函数就是一个对上面学习的一个汇总.

    闭:指的该函数是定义在函数内的函数

    包:指的就是该函数引用了一个外层函数作用域的名字

    例子1:执行了outter函数就是相当于执行了wrapper(),即在全局名称空间执行到了局部名称空间内的代码

    def outter():
        x = 111  # x表示的就是包
        def wrapper():  # wrapper表示就是闭
            print(x)
        return wrapper 
    
    f = outter()
    print(f)  # 111
    

    闭包函数其实本质就是为函数体代码传参的一种方式

    我们之前都学过,为函数体代码传参最直接的方式就是用参数传

    即例子:

    def wrapper(x):
        print(x)
    
    wrapper(111)
    wrapper(222)
    wrapper(333)
    

    那我们还能不能通过别的方式为函数体代码传参呢??

    闭包函数就是这样的一种方式

    即:案例

    def outer(x):
        # x = xxx
        def wrapper():
            print(x)
    
        return wrapper
    
    f1 = outer(111)
    f1()  # 111
    
    f2 = outer(222)
    f2()  # 222
    

    装饰器:

    1.什么是装饰器

    ​ 装饰器就是一个用来为被装饰对象添加新功能的工具

    2.为何要用装饰器

    ​ 开放封闭原则:一旦软件上线运行之后,应该对修改源代码封闭,对扩展功能开放

    原则:
    1、不修改函数内的源代码
    2、不修改函数的调用方式
    装饰器就是在遵循原则1和2的前提下,为被装饰对象添加上新功能

    3、如何实现装饰器

    我们先不说如何实现,我现在有一个需求:为函数index添加统计运行时间的功能

    import time
    
    def index():
        time.sleep(1)  # 模拟功能在做一些io操作
        print('from index')
    
    index()
    

    是不是很简单,那我现在又接收到了一个需求:让我们给index函数在不动它函数体代码的情况下,为其添加计时功能?

    这时候你可能会问???我为什么要不动函数体代码.是的,一般我们也正是如此,但有时候啊,你一个软件以及上线了,你如果要直接该源代码的话,你可能会因为你一改,改对了还好,该运行的还正常运行,当时当你改错了,你会发现,你有多个地方已经改了,你的程序有可能就直接崩了.而原代码还被你给改了,这就导致了你程序要进入维修状态了.

    而装饰器呢就可以解决不改变函数体源代码的情况下,为其添加功能.

    居然装饰器这么强大,那我们赶快学习吧.

    不要着急.我们在学习的前提,我们看看我们对函数index加上计时功能的几种解决方案

    方案一:在index函数的函数体代码内添加功能代码

    # 代码示范
    import time
    
    def index():
        start = time.time()
        time.sleep(1)
        print('from index')
        stop = time.time()
        print('run time is %s' %(stop - start))
    
    index()
    

    你会发现,是的你已经添加了计时功能了,但是有一个弊端就是动了函数体代码.

    方案二:在调用函数时,再为其加上计时功能

    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    start = time.time()
    index()
    stop = time.time()
    print('run time is %s' %(stop - start))
    

    你会说这时候我没改函数体代码了吧,但是你这时候又犯了另外一个弊端,你要在每次执行index的时候带上对应的功能代码,重复代码可不是一个好的习惯.

    此时你又可以想到,我可以将下面的也放到一个函数内啊,每次执行新的函数不就好啦!

    也就是方案三:将index函数放到另外一个函数内,添加计时功能,再调另外一个函数保证添加计时功能

    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    def wrapper():
        start = time.time()
        index()
        stop = time.time()
        print('run time is %s' %(stop - start))
    
    wrapper()
    wrapper()
    

    但你有没有发现你的调用方式又变了,你又得去,将每个调用index的改成调用wrapper了.

    我们根据上面的方案三,我又有一个需求,将计时添加到多个功能上面

    所以就有了方案四:将计时功能添加到多个函数上面

    import time
    
    def index():
        time.sleep(1)
        print('from index')
    
    def wrapper(func):
        start = time.time()
        func()
        stop = time.time()
        print('run time is %s' %(stop - start))
    
    wrapper(index)
    

    但是你还是没满足我的不改变调用方式的需求:

    这时候你就会想到你学过的闭包了

    案例5,将计时功能添加到别的功能上,并且不改变功能调用的形式

    提示:案例5最好自己实现.

    import time
    
    
    def index():
        time.sleep(1)
        print('from index')
    
    
    # wrapper 是 一个 闭包函数 , 闭的是 wrapper函数 即 timer函数内部定义的一个函数, 包指的是wrapper函数引用了外部函数timer的局部名称,即作用域名字==>func
    def timer(func):
        """
        使wrapper函数变成即使是一个局部的名称空间,但是在全局也可以调用
        :param func: func 即要加上 该功能的 函数
        :return: 返回的就是wrapper的内存地址---> 加() 即可以执行内部代码
        """
    
        def wrapper():
            """
            作用是为func函数加上计时功能
            :return: None
            """
            start = time.time()
            func()
            stop = time.time()
            print('run time is %s' % (stop - start))
    
        return wrapper
    
    
    index = timer(index)  # 即将index这个函数当作实参传给了timer, 拿到wrapper函数的内存地址.再将内存地址绑定了一个名称为index
    index()  # 执行了index 内存地址对应的函数代码体  注意:此时这个index 对应的内存地址是timer返回的内存地址
    

    这时候,你就已经满足了我的所以需求了.不仅加了功能还没改变调用方式

    先给你留个悬念:其实在python中index = timer(index),还有一个简单的使用方式.明天再讲.

  • 相关阅读:
    关联模型(1:n)
    关联模型 (1对1)
    cheerio api
    二维数组去重
    Fiddler 模拟post 提交
    DataReader 转为实体
    在做一些复杂的类型转换之前(比如将一个数据转换成一个属性的类型,属性可能为可空类型)先判断该类型是否为可空类型,否则会报如下错误:
    WebClient 文件下载
    利用iTextSharp组件给PDF文档添加图片水印,文字水印
    TList、DataTable To Json
  • 原文地址:https://www.cnblogs.com/jkeykey/p/14207637.html
Copyright © 2020-2023  润新知