前后端知识库 前后端知识库
首页
    • JavaScript
    • React
    • Vue
  • Python
  • Sanic
  • Linux
  • Ansible
归档
GitHub (opens new window)
首页
    • JavaScript
    • React
    • Vue
  • Python
  • Sanic
  • Linux
  • Ansible
归档
GitHub (opens new window)
  • Sanic

  • Python

    • Python 的动态类型介绍
    • 数字类型
    • Set类型
    • 字符串
    • 列表
    • 元组
    • 通用序列
    • 字典
    • 文件
    • 语句和语法
    • 赋值语句
    • 打印
    • if语句
    • loop循环
    • 迭代器和生成器
    • 文档
    • 函数
    • 作用域
    • 参数
    • 函数的高级特性
    • 模块
    • 模块包
    • 模块高级用法
    • 类 class
    • Python命名空间
    • 运算符重载
    • 类的设计模式
    • 类的高级主题
    • 异常
    • 异常对象
    • Unicode与字节串
    • 管理属性
    • 装饰器
    • 元类
    • 执行细节
  • backend
  • Python
devin
2023-09-09

作用域

# 作用域

1.代码中变量名被赋值的位置决定了这个变量名的作用域(即可见范围)。因为变量是在首次赋值的时候定义的。

  • Python 将一个变量名首次被赋值的地点关联为一个特定的命名空间

    2.变量可以在 3 个不同的地方定义,对应三种不同的作用域:

  • def内赋值定义的变量:作用域为本函数

    这里的赋值包括显式=赋值和隐式赋值。隐式赋值包括

    • import语句隐式赋值
    • def函数定义来隐式赋值(函数名这个变量)
    • 形参匹配来隐式赋值
  • 嵌套的def中赋值定义的变量:对于父函数来说,该变量不是本地的

  • def之外赋值,作用域为整个文件全局的
    三种作用域

    3.作用域法则:

  • 每个模块文件就是一个全局作用域。从外面看,模块的全局变量就成为该模块对象的属性;从内部看,模块的全局变量就是普通的、作用域为全局的变量

  • 全局作用域的范围仅限于变量所在的单个文件

  • 每次调用函数,都创建一个新的局部作用域

  • 默认情况下,所有函数定义内部的变量都是局部变量

    • global语句声明会将变量名作用域提升至全局
    • nonlocal语句声明会将变量名作用域提升至外层的def
  • Python 预定义的_builtin_模块提供了一些预定义的内置变量
    作用域法则

    4.交互式运行的代码实际上位于__main__的内置模块中 5.变量名查找规则:LGBE

  • 首先查找本地作用域L

  • 接着查找上一层def或lambda的本地作用域E

  • 接着查找全局作用域G

  • 最后查找内置作用域B

如果均未找到变量名则报错。
当前面作用域的变量名覆盖内置的作用域名字时,可以手动import builtins模块,在用builtins.name来直接使用这个变量名。注意不要随意修改builtins模块内变量的值。

a=1
b=2
def func():
	global a,b,c #一个名字或逗号分隔的多个名字,这些变量名被提升到全局作用域内
	a=2
	b=0
	c=0 #虽然c没有在def外定义,但这里的global 和 c=0会在全局作用域内定义c
1
2
3
4
5
6
7

global用法

7.要在局部作用域中修改全局变量,方法有以下几种:

  • global 声明

  • 通过import modname然后利用modname.attr来访问

  • 通过import sys然后利用sys.modules['modname'].attr来访问
    global用法

    8.作用域示例:

x='global_x'          #全局作用域
def f1():
  x='f1_x'            #f1的局部作用域
  z='f1_z'            #f1的局部作用域
  def f2():
	global y      #全局作用域
	y='f2_y'
	print('in f2',x) #LGBE法则,找到的是f1局部作用域中的x
	nonlocal z    #f2的nonlocal作用域,是f1的局部作用域
   	z='f2_z'
	t='f2_t'      #f2的本地作用域
  f2()
1
2
3
4
5
6
7
8
9
10
11
12

作用域示例

9.嵌套的函数返回后也是有效的。

def f1():
  x=99
  def f2():
      print(x)
  return f2 # f2是个局部变量,仅在f1执行期间因为新建了局部作用域所以才存在。
action=f1() #f2指向的函数对象,由于action引用它,因此不会被收回。
#但是f2这个位于局部作用域中的变量名被销毁了
action() #此时f1执行结束,但是f2记住了在f1嵌套作用域中的x。这种行为叫闭包
1
2
3
4
5
6
7
8

类比闭包在语义上更明确,因为类可以明确定义自己的状态

闭包

10.在函数内部调用一个未定义的函数是可行的,只要在函数调用是,该未定义函数已定义即可。
调用未定义函数

11.嵌套作用域中的变量在嵌套的函数调用时才进行查找,而不是定义时。 、

def func():
	acts=[]
	for i in range(5):
		acts.append(lambda x:i**x) #添加匿名函数对象
	return acts
acts=func()
acts[0](2) #调用时才开始查找i,此时i最后被记住的值是4
1
2
3
4
5
6
7

嵌套作用域中变量名查找

要解决这个这个陷阱,可以用默认参数:

def func():
	acts=[]
	for i in range(5):
		acts.append(lambda x,i=i:i**x) #每次循环,形参i的默认实参均不同
	return acts
acts=func()
acts[0](2)
1
2
3
4
5
6
7

12.nonlocal是 Python3 中引入的。

def func():
  a=0
  b=1
  def func1():
	nonlocal a,b#限定了查找只能在func1所在的作用域(即func的本地作用域),且要求名字a,b已经存在。
	a='a' #对a,b的赋值会影响到func中的a,b
1
2
3
4
5
6
  • 如果没有nonlocal,则在func1中的赋值会创建本地变量,而无法修改func中的局部变量

  • global使得作用域查找先从全局作用域开始,跳过了局部作用域以及nonlocal作用域。

  • Python 在函数创建的时候解析nonlocal,而不是在函数调用时,因此要求nonlocal的名字已经存在

    13.全局变量、nonlocal变量、类、函数属性都提供了状态保持能力

    14.global、nonlocal只是修改了变量作用域(即作用域提升),但是并未给出变量定义
    global|nonlocal不是定义

编辑 (opens new window)
上次更新: 2023/09/09, 12:09:00
函数
参数

← 函数 参数→

Theme by Vdoing | Copyright © 2023-2023 devin | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式