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
posted @ 2019-10-18 19:15  zx125  阅读(275)  评论(0编辑  收藏  举报