zoukankan      html  css  js  c++  java
  • python函数对象-命名空间-作用域-02

    函数对象

    函数是第一对象: python 中万物皆对象,函数同样也是 python 中的对象 、 函数作为对象可以赋值给一个变量、可以作为元素被添加到容器对象中、可以作为参数传给其他函数、可以作为函数的返回值(这些特性就是第一类对象所特有的) 

    函数对象的特性(*****灵活运用,后面讲装饰器会用到)

      函数名可以像变量一样被传递

    # 变量可以被传递
    name = 'jason'
    x = name
    print(x)
    # jason
    print(id(name), id(x))
    # 3085464224688 3085464224688
    
    # 尝试函数像变量一样被传递
    def func():
        print('from func')
    print(func)
    # <function func at 0x0000016E5E062E18>
    
    f = func  # 其实指向的也是函数func 指向的函数体代码的内存地址
    print(f)
    # <function func at 0x000001B4D0D92E18>
    f()
    # from func
    print(id(func), id(f))
    # 3085463137816 3085463137816

      函数名可以被当做参数传递给其他函数

    def func():
        print("from func")
    
    def index(args):
        print(args)
        args()  # 函数只要一定义(先定义),就可以在任意地方调用
        print("from index")
    
    index(func)
    # <function func at 0x000001B7429A2E18>
    # from func
    # from index

      函数名可以被当做函数的返回值

    def index():
        print('index')
    
    def func():
        print('func')
        return index
    
    res = func()  # 将返回的函数名index 赋值给变量res
    # func
    print(res)
    # <function index at 0x000001EF64362E18>
    res()  # 函数名加括号(...内存地址)就可以直接调用该函数
    # index

      函数名可以被当做容器类型的参数

    def func():
        print('func')
    
    l = [1, 2, func, func()]  # 定义的时候默认执行了func(),所以下面先打印了 func
    # func  # func函数没有返回值,默认返回None,所以列表 l 的第四个元素就是None
    print(l)
    # [1, 2, <function func at 0x0000013931C92E18>, None]

    函数对象小练习

    题目: # 循环打印项目功能提示信息 供用户选择 用户选择谁就执行谁 

    def register():
        print("注册了")
        pass
    
    
    def login():
        print("登录了")
        pass
    
    
    def shopping():
        print("购物了")
        pass
    
    
    def output_func_list():
        print("----- 请选择功能!")
        for key in func_list:
            print(f"----  {key}.{func_list[key][1]}")
    
    
    func_list = {
        0: [register, '注册'],
        1: [login, '登录'],
        2: [shopping, '购物'],
    }
    
    
    while True:
        output_func_list()
        chose_func = input("请输入功能编号(q 退出系统):").strip()
        if chose_func.isdigit():
            # 执行相应功能
            chose_func = int(chose_func)
            # 判断输入的编号在不在功能列表里
            if chose_func in func_list:
                func_list[chose_func][0]()  # 取到功能函数名,加括号调用
            else:
                print("您输入的功能编号不存在,请重新输入!")
        elif chose_func.lower() in ['q', 'quit']:
            print("感谢您的使用,祝您生活愉快~")
            break
        else:
            print("请正确输入数字!")
    实现代码

    知识点: # 函数名可以作为容器对象的元素值 , # 函数名(即函数内存地址)可以加括号直接调用 

      上述其他三个特性在装饰器中会有灵活运用,就暂不举例了

    函数的嵌套调用与定义

    嵌套调用

    函数的嵌套调用: # 在函数内部调用其他函数 

    def index():
        print('index')
    
    def func():
        index()  # 在定义 func 函数的时候不会直接调用 index 的方法 --> 函数定义的时候不执行代码
        print('func')
    
    func()
    # index  # 通过 func()函数内部调用了index() 函数,打印出了 index
    # func

    函数的嵌套调用可以 # 将复杂的逻辑简单化 

      小练习: # 写一个函数可以求四个数中的最大值(不许用内置函数 max) 

    def my_max(x, y):
        if x > y:
            return x
        return y
    
    def my_max4(a, b, c, d):
        res = my_max(a, b)
        res = my_max(res, c)
        res = my_max(res, d)
        return res
    
    print(my_max4(1, 5, 7, 1))
    # 7
    案例代码

    嵌套定义

    def outer():
        x = 1
        print("outer")
        def inner():
            print("inner")
        inner()
    
    # inner()  # 会报错,在外部无法访问内部内容
    outer()
    # outer
    # inner

    实现在外部调用 outer函数的内部函数 inner

    # 想在外部调用inner 可通过把内部的函数名当做外部函数的返回值来返回给外部
    def outer():
        x = 1
        print("outer")
        def inner():
            print("inner")
        return inner  # 把 inner 函数当做函数的返回值返回给 outer函数的调用者
    
    res = outer()
    # outer
    res()  # 变相调用inner
    # inner
    实现代码

    小案例: # 写一个函数,该函用户可以通过传参的不同 控制函数指向不同的功能 

    def all_func(type):
        def register():
            print('register')
        def login():
            print('login')
        def shopping():
            print('shopping')
    
        if type == 1:
            register()
        if type == 2:
            login()
        if type == 3:
            shopping()
    
    all_func(1)
    all_func(2)
    all_func(3)
    # register
    # login
    # shopping
    实现代码

    命名空间(又叫名称空间)(****绕且重要)

     命名空间: # 存放的是变量名与变量值的内存地址绑定关系的地方 

     访问变量的值: # 要想访问一个变量的值,必须先去命名空间拿到对应的名字,才能访问变量的值 

    命名空间的分类

     命名空间分为: # 内置命名空间、全局命名空间、局部命名空间 三大类

     内置命名空间

    内置命名空间: # python 解释器提前已经定义好了的名字(已经存放到了内置命名空间中了) 

    print("hello world")
    max(1, 44, 62, 15)
    len('26515f1asfafqw')
    sum([1, 2, 3, 4, 5])
    # 像上面的print max len sum 并没有定义就可以值使用,它们就是python解释器提前定义好了的函数,属于内置命名空间的

     全局命名空间

     全局命名空间: # 文件级别的代码 

    x = 1
    if x == 1:
        y = 2
    print(y)
    # 2
    
    for i in [1, 2]:
        print(i)
    print(i)
    # 1
    # 2
    # 2
    
    # 上面的 x y z 都在全局命名空间,不要以为缩进的就是局部的(if、 for、 while 无论嵌套,多少层,他们内部所创建的名字都是全局命名空间的)

     局部命名空间

     局部命名空间: # (目前所学)函数体内创建的名字都属于局部命名空间(最外层的函数名是属于全局命名空间的) 

    def func():
        username = 'jason'
    # print(username)  # 会报错 NameError: name 'username' is not defined ,username 这个变量在 func 这个函数的命名空间里,在全局命名空间里访问不到它
    func()

      至于为什么上面的 print(username) 为什么会报错,学完下面的知识你就知道啦。

    命名空间的生命周期

    '''
    命名空间的生命周期
        内命名空间:(最长)只要 python解释器启动,立马创建  关闭 python解释器时自动销毁
        全局命名空间: 只要右键运行 py文件就会自动创建  py文件程序运行结束自动销毁
        局部命名空间:(动态创建动态销毁)函数被调用的时候自动创建  函数执行结束后立即销毁
    '''

    补充:与垃圾回收机制的关系

    # 命名空间生命周期结束 -- >  里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收
    # del 删除变量 -- >  里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收  ---> 等同于命名空间里删除了一个变量(绑定关系)
    # 垃圾回收机制:垃圾回收机制隔一段时间就会检查一次,内存中的值如果没有变量指向它(引用),那垃圾回收机制就会把它清除掉(释放内存)
    #             如果多次检查都有变量等指向它,那就会把它等级提升,检查频率就会变低

     命名空间的查找顺序

     验证思路: # 找一个三个地方都有的东西来验证(比如 len、max等,暂时忽略命名规范不能与关键字重复) , # 分别注释来测试其查找顺序(全局、局部) 

     验证过程

    len = '我是全局命名空间的len'
    def func():
        len = '我是局部命名空间的len'
        print(len)
    print(len)  # 这里是全局的位置
    # 我是全局命名空间的len
    '''
    # 把全局的len 注释掉,就去找了内置的len
    print(len)  # 是全局的位置
    # <built-in function len>
    '''
    func()
    # 我是局部命名空间的len

     大致结论:

    '''
    (******)命名空间的查找顺序
        1.需要先确定当前的在哪(全局、局部),大前提
            1.1 站在全局:全局 >>> 内置
            1.2 站在局部:局部 >>> 全局 >>> 内置
              1.2.2 站在局部的内部(多个局部嵌套):局部 >>> 上一级局部 >>> 上一级局部 >>> .... >>> 全局 >>> 内置 
                  会在作用域同级的前后(这句代码前后的同级语句)去找,然后再上一级
        2.函数在定义阶段查找名字的顺序(范围)就已经固定了, 不会因为函数的调用位置变化而变化(*******)
              可以在函数定义的时候写个注释,指出他查找的位置,防止逻辑复杂了搞不清楚
    '''

    加深理解的小案例

    # 通过注释不同函数层内的x 来加深理解命名空间查找顺序(可以采用收起(折叠)代码块的技巧来快速指定)
    x = 111
    def f1():
        x = 222
        def f2():
            x = 333
            def f3():
                # x = 444
                def f4():
                    # x = 555
                    print(x)  # 这个案例在本局部找到了 变量x, 所以用的是内部的这个 777,在调用前定义了,所以不会报错
                x = 777  # 纯粹为了教学演示
                f4()
                x = 777  # 纯粹为了教学演示
            f3()
        f2()
    f1()
    # 777
    案例一(可多次练习)
    def func():
        x = 1
        def index():
            print(x)  # 查找顺序:本作用域找x,没找到,上一级func里找,找到了,那就引用的是func 作用域里的 局部变量x
        return index
    
    res = func()
    x = 999
    res()
    # 1
    案例二
    x = 111
    def outer():
        def inner():
            print('from inner', x)  # 查找顺序:函数体inner 内上下没有x,再找 outer里面,也没有x, 那就找全局,找到了x,所以这里的x 就是全局的x
        return inner
    f = outer()
    x = 222  # 调用前改变了全局 x 的值,所以最后结果是 222
    f()
    # from inner 222
    案例三
    x = 111
    def outer():
        def inner():
            print('from inner', x)  # 查找顺序:函数体inner 内上下没有x,再找 outer里面,也没有x, 那就找全局,找到了x,所以这里的x 就是全局的x
        return inner
    f = outer()
    def func():
        x = 333  # 没有global 关键字指定x 为全局变量,这里是重修申请了一个局部变量 x,这里并不会影响 全局的那个x
        f()
    func()
    # from inner 111
    案例四
    # 下面这个案例会直接报错
    x = 111
    def outer():
        def inner():
            print('from inner', x)  # 会直接报错,UnboundLocalError: local variable 'x' referenced before assignment ---> 本地变量(局部变量)x 在定以前被引用了
            # 报错原因: ---> 查找顺序:函数体 inner内部有 x, 所以这个print 内x 指定的是x = 66666666的那个局部变量,而调用print 时,他还没定义出来
            x = 66666666
        return inner
    f=outer()
    f()
    案例五(讲原理,忽略智能语法报错)

    案例一原理图

    作用域

    python中的作用域有 全局作用域 与 局部作用域 , 全局作用域: # 全局有效: 内置命名空间、全局命名空间 都属于全局作用域 , 局部作用域: # 局部有效:局部命名空间 

      局部修改全局变量(修改和访问是两回事)

    # 尝试修改不可变类型的全局变量
    x = 1
    def func():
        x = 2  # 实质是又创建了一个局部变量 x
    func()
    print(x)  # 局部无法修改不可变类型的全局变量
    # 1
    
    # 尝试修改可变类型的局部变量
    x = []
    def func():
        x.append('嘿嘿嘿')
    func()
    print(x)  # 修改成功,局部可以修改可变类型的全局变量
    # ['嘿嘿嘿']
    
    # 全局访问不了局部的变量,所以不展开探讨

        小结论: # 局部无法修改不可变类型的全局变量 , # 局部可以修改可变类型的全局变量 (前提:在不使用 global  和  nonlocal  关键字的情况下)

      通过  global  关键字在局部修改全局,修改多个用 , 隔开

    x = 1  # 不可变类型
    username = 'jason'
    def func():
        global x,username
        x = 999  # 修改全局变量,而不是创建局部变量
        username = 'egon'
    func()
    print(x, username)
    # 999 egon

      通过  nonlocal  关键字在局部修改局部,修改多个用 , 隔开

    def func():
        x = 1
        def index():
            x = 2
        index()
        print(x)
    func()
    # 1
    
    # 想就在 index 里把 x 改了
    def func():
        x = 1
        def index():
            nonlocal x
            x = 2
        index()
        print(x)
    func()
    # 2

     小扩展(python之禅)--- 娱乐了解

     import this 

  • 相关阅读:
    SI与EMI(一)
    设计上如何避免EMC问题
    EMC与地之重新认识地
    EMC学习之电磁辐射
    围殴拓扑和端接之终结篇
    T型及Fly_by拓扑之应用总结
    拓扑结构介绍及其种类
    [转]Verilog综合时wire和reg如何防止被优化
    Verilog基础知识0(`define、parameter、localparam三者的区别及举例)
    [转]jumbo frame介绍
  • 原文地址:https://www.cnblogs.com/suwanbin/p/11164596.html
Copyright © 2011-2022 走看看