关于python的作用域(Scope Resolution)

1、四种作用域及其生命周期:

(1)local(本地局部作用域),即函数中定义的临时变量,当函数结束时,变量的生命周期结束。

(2)enclosed(闭包,嵌套的父级函数的局部作用域),即闭包外层函数的局部变量,外层函数结束,变量的生命周期结束。

(3)global(全局变量),即模块级别定义的变量,模块销毁,变量的生命周期才会结束。

(4)bulit-in(内置函数)是python解释器,虚拟机内置的变量。

搜索变量的顺序是:(1)->(2)->(3)->(4)

2、global关键字的使用

(1)未使用关键字global的情况下,全局变量在局部作用域里默认是只读的(无法修改),如果为其赋值一个新的值,python认为是在当前的局部作用域里创建一个新的“同名”变量

x = 10
def f1():
	x = 20
	print(x)
f1()
print(x)
"""
Output:
20
10
"""

(2)在确认使用全局变量的情况下,但是又没有关键字的情况下,如果试图修改全局变量,会报UnboundLocalError的错误!!!

x = 10
def f1():
	print(x)  # 确认使用外部作用域
	x = 2
#UnboundLocalError: local variable 'x' referenced before assignment

(3)当局部作用域想修改全局变量时,使用关键字global即可

x = 10
def f1():
    global x
	x = 20
	print(x)
f1()
print(x)
"""
Output:
20
20
"""

(4)使用global声明,不能放在使用变量之后,会报错IndentationError

x = 10
def f1():
	x = 20
    global x  # IndentationError: unindent does not match any outer indentation level
	print x
f1()
print x

(4)此时你会发现,global只能用作全局作用域的变量,非全局作用域的外层变量好像无效!!!

def f1():
    x = 10
    def f2():
        global x  # 如果用global,你会发现修改x失败
        x = 20
        print(x)
    f2()
    print(x)
f1()
"""
Output:
20
10
"""

此时怎么办呢?如果你使用的python2,那么可以使用下面这个方法,把非全局作用域的外层变量变成全局作用域变量…(有点傻但是容易理解)

def f1():
    global x  # 这个地方将x声明为全局作用域的变量,他的声明周期也就变了。后续使用global声明也能影响到了
    x = 10
    def f2():
        global x
        x = 20
        print(x)
    f2()
    print(x)
f1()
"""
Output:
20
20
"""

如果你是用的是python3,那就简单了!直接使用nonlocal关键字!!!

def f1():
    x = 10
    def f2():
        #global x  # 如果用global,你会发现修改x失败
        nonlocal x
        x = 20
        print(x)
    f2()
    print(x)
f1()
"""
Output:
20
20
"""

(5)在python中只有模块(module),类(class)以及函数(def, lambda)才会引入新的作用域,其他的模块(if, try, for)不会引入新的作用域

if True:
	x = 1
print x
"""
Output:
1
"""

(6)虽然if模块不会引入作用域,但是依旧要注意其逻辑。未执行的代码,变量不存在

if False:
	x = 1
print x  # ERROR

3、踩坑的地方

(1)Enclosed,闭包踩坑

fs = []
for x in range(3):
    print(x)
    fs.append(lambda :x)
print("------")
for f in fs:
    print(f())
"""
Output:
0
1
2
------
2
2
2
"""

为什么会有这个坑?

因为闭包的对象都是引用而非临时变量。

在上面代码中,添加进list中的x可以简单理解成,x是指向数值1,2,3的一个指针~

随着for循环的进行,x的指向一直在改变,打印list中的值也就改变了。

如何解决这个问题???

重新定义一个函数,强行让其传递一个不会一直改变指向的指针(新的临时变量)。

fs = []
def gen(x):
    return lambda :x
for x in range(3):
    fs.append(gen(x))
for f in fs:
    print(f())

(2)Function arguments,函数形参

def func(data = []):
    data.append(1)
    return data
print(func())
print(func())
print(func())
"""
Output:
[1]
[1, 1]
[1, 1, 1]
"""

为什么会这样呢?

试着查看一下每次返回的列表的 ID,发现都一样。

def func(data = []):
    data.append(1)
    return data
"""
Output:
1870100386312
1870100386312
1870100386312
"""

那原因就很简单了,func函数在不同的函数调用中一直在使用同一个列表对象。

为什么会一直使用同一个列表对象呢?

答案是:默认参数语句,总是在 def 关键字定义函数的时候被求值,且仅执行这一次!!!

仅执行一次可以查阅 Python 语言参考(The Python Language Reference) 的相关章节:

https://docs.python.org/zh-cn/3.7/reference/compound_stmts.html#function-definitions

默认形参值会在执行函数定义时按从左至右的顺序被求值。这意味着当函数被定义时将对表达式求值一次,相同的“预计算”值将在每次调用时被使用。这一点在默认形参为可变对象,例如列表或字典的时候尤其需要重点理解:如果函数修改了该对象(例如向列表添加了一项),则实际上默认值也会被修改。】

绕过此问题的一个方法是使用 None 作为默认值,并在函数体中显式地对其进行测试,例如:

def func(data = None):
    if data is None:
        data = []
    data.append(1)
    return data
print(func())
print(func())
print(func())
"""
Output:
[1]
[1]
[1]
"""

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python中的作用域指的是变量的可访问性和可见性范围。Python按照LEGB原则搜索变量,即优先级L>E>G>B。LEGB分别代表Local(局部作用域)、Enclosing(闭包函数外的函数中)、Global(全局作用域)、Built-in(内置作用域)。在变量引用时,Python会按照LEGB的顺序搜索变量。如果在局部作用域找不到变量,Python会继续搜索封闭函数的作用域,再继续搜索全局作用域,最后搜索内置作用域。另外,如果发生变量冲突,可以使用`del`语句从全局作用域中删除变量的定义,从而访问内置作用域中的变量。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [python中的各种作用域](https://blog.csdn.net/weixin_44835655/article/details/127183921)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Python作用域](https://blog.csdn.net/daiominribao/article/details/53301594)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值