zoukankan      html  css  js  c++  java
  • Python作用域

    Python的作用域

    参考骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9925021.html#blogaaa2

    4大作用域

    内置作用域dir(builtins)->全局作用域dir()->外部函数的本地作用域->嵌套函数内部的本地作用域

    x=1
    def f(i):
        x=3
        g()
        def r():
            x=2
            print("r:",x)
        print("f:",x)   # 3
        r()
    
    def g():
        print("g:",x)   # 1
    
    f(5)
    print("main:",x)
    
    g: 1
    f: 3
    r: 2
    main: 1
    

    作用域分析

    处于全局作用域(模块级别)范围的变量有:x=1,f,g

    处于f本地作用域的变量有:i,x=3,r

    处于嵌套函数r的本地作用域的变量有:x=2

    全局变量

    关于python中的全局变量:

    • 每个py文件(模块)都有一个自己的全局范围
    • 文件内部顶层的,不在def区块内部的变量,都是全局变量
    • def内部声明(赋值)的变量默认是本地变量,要想让其变成全局变量,需要使用global关键字声明
    • def内部如果没有声明(赋值)某变量,则引用的这个变量是全局变量

    global关键字

    正常写法

    相当于在zx中声明一个局部变量x=2,所以没改变全局变量

    x = 3
    def zx():
        x = 2
    
    zx()
    print(x)
    
    3
    

    global

    相当于在zx中把x声明为全局变量,所以可以直接在zx中操作改变全局变量的值

    global就是声明变量的名称空间,告诉zx这个x是全局的,不是你的

    x = 3
    def zx():
        global x
        x = 2
    
    zx()
    print(x)
    
    2
    

    错误

    这句话是错误的,而且是语法错误!

    def f():
        y=2
        global y
    

    全局变量是不安全的

    当使用多线程的时候,全局变量是共享的,每个线程都能操作这个数据,所以是不安全的,需要自己加锁

    模块全局变量

    主要思路就是,模块导入只会执行一次,其他都是从内存中拿的,所以后面两次都是+2,都是拿的同一个模块对象

    b.py

    x=3
    
    def f():
        global x
        x += 2
    
    def f1():
        x=4        # 本地变量
    
    def f2():
        x=4             # 本地变量
        import b
        b.x += 2  # 全局变量
    
    def f3():
        x=4            # 本地变量
        import sys
        glob = sys.modules['b']
        glob.x += 2    # 全局变量
    
    def test():
        print("aaa",x)            # 输出3
        f();f1();f2();f3()
        print("bbb",x)        
    

    a.py

    import b
    b.test()
    
    aaa 3
    bbb 9
    

    超级错误案例解析

    例1

    x=1
    def g():
        print(x)
        x=3
    g()
    
    print(x)
    
    UnboundLocalError: local variable 'x' referenced before assignment
    

    错误分析

    python不是读一行运行一行的吗?这里为啥会报错?不是应该去打印全局作用域的x吗?

    相信你也会有这些问题吧!首先我们来看看打印的错误。赋值前引用了局部变量x,说明并没有去全局找x,而是在本地拿了值,但是这个变量还没有赋值才出现的错误

    原因:

    函数的运行并不是我们想的那样的,每个函数属于一个区块,这个区块是一次性解释的,不是读就执行一行的,而是一直读完整个区块,再去执。当读完整个区块,记住了x=3这句话,将重新定义本地变量x,当执行print(x)的时候,区块告诉它我有x值,不用出去找了,于是print(x)信了,打印的时候发现没有值啊,区块骗我!

    例2

    x=3
    
    def f1():
        x += 3
        print(x)
    f1()
    
    x += 3
    UnboundLocalError: local variable 'x' referenced before assignment
    

    原因:

    这个错误和上面其实是一样的,把x+=3当成是x=x+3,继续上面思路,当读到x+=3的时候,它把它当做了一个普通的变量声明和赋值,然后进行区块执行的时候是先右边开始运行所以先运行x+1,这个时候x并没有值,所以出错了。

    局部作用域

    nonlocal关键字

    nonlocal关键字类似global,但是它是局部名称空间相对于,局部名称空间中的名称空间

    x=3
    
    def f1():
        x=4          # f1的本地变量
        def f2():
            x=5      # f2的本地变量
            def f3():
                nonlocal x  # f2的本地变量
                print("f3:",x)  # 输出5
                x=6
            f3()
            print("f2:",x)  # 被修改,输出6
        f2()
    f1()
    
    f3: 5
    f2: 6
    

    但是局部嵌套有多层的特点,他会一直向上找,但是不会找到全局作用域范围

    x=3
    
    def f1():
        x=4
        def f2():
            def f3():
                nonlocal x      # f1()的本地
                print("f3:",x)  # 输出4
                x=6             # 修改f1()的本地
            f3()
            print("f2:",x)   # 输出6
        f2()
        print("f1:",x)       # 输出6
    f1()
    
    f3: 4
    f2: 6
    f1: 6
    

    在没有nonlocal的时候是怎么解决问题的

    x=3
    def f1():
        x=4
        def f2(x=x):
            x += 3
            print("f2:",x)
        x=5
        f2()
        print("f1:",x)
    f1()
    
    f2: 7
    f1: 5
    

    来说一下fe()函数执行的过程,因为是函数,所以是个区块,先读完整个区块的代码,读到x=x就相当于,知道区块了一个 x变量,然后开始运行,给x变量赋值为4,f2中是有个 局部变量的

    避免函数嵌套

    嵌套函数

    def f1():
        x=3
        def f2():
            nonlocal x
            print(x)
        f2()
    f1()
    
    3
    

    尽量写成

    def f1():
        x=3
        f2(x)
    
    def f2(x):
        print(x)
    
    f1()
    
    3
    

    循环内部函数

    运行f1,f1中i=4,有一个n函数对象,当真正执行n函数,首先读一遍块区,知道n里面有一个i变量,然后执行print(i),才会去找i的值。当循环结束,n中的i指向i的最后一个元素i的地址

    def f1():
        for i in range(5):
            def n():
                print(i)
        return n
    
    f1()()
    
    4
    

    所以这里5个函数中的i都是指向最后一个元素i的地址

    def f1():
        list1 = []
        for i in range(5):
            def n(x):
                return i+x
            list1.append(n)
        return list1
    
    mylist = f1()
    for i in mylist: print(i)
    print(mylist[0](2))
    print(mylist[2](2))
    
    <function f1.<locals>.n at 0x02F93660>
    <function f1.<locals>.n at 0x02F934B0>
    <function f1.<locals>.n at 0x02F936A8>
    <function f1.<locals>.n at 0x02F93738>
    <function f1.<locals>.n at 0x02F93780>
    6
    6
    

    如果我们就想要这种效果呢?那么就使用传参的方法

    def f1():
        list1 = []
        for i in range(5):
            def n(x,i=i):
                return i+x
            list1.append(n)
        return list1
    
    for i in f1():
        print(i(3))
        
    3
    4
    5
    6
    7
    
  • 相关阅读:
    常用的标签分类
    css 实现动态二级菜单
    5大主流浏览器内核
    MySQL里面的子查询
    Algolia Search
    Nginx配置
    PHP中Abstract与Interface区别
    Shell 基本语法
    百度 echarts K线图使用
    php_soap扩展应用
  • 原文地址:https://www.cnblogs.com/zx125/p/11700245.html
Copyright © 2011-2022 走看看