深入了解Python 变量作用域


Posted in Python onJuly 24, 2020

特点

python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。Python中并不是所有的语句块中都会产生作用域。只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。

1. 函数内部的变量,函数外部不能访问

def func():
  variable = 100 
  print(variable) 
print(variable) # name 'variable' is not defined

2. 函数上层的变量(标量)只能读取,不能再次定义,初始化

def counter1():
  n = 0
  def compute():
    n = n + 1 # n为标量(数值,字符串,浮点数),Python程序会因为“如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改.那么python会认为它是一个局部变量,又因为函数中没有n的定义和赋值,所以报错
    # y = n + 1 # 更改为y就没事
    # return y
    return n
  return compute
variable = 300
def test_scopt():
  print(variable) # 此时调用局部变量variable并有没绑定到一个内存对象(没有定义和初始化,即没有赋值)。本质上还是遵循的LEGB法则
  variable = 200 #因为这里,前面调用过一次,所以variable就变为了局部变量
  # print(variable) # 写在下面就没问题,因为variable是新的局部变量,而不是重新被定义,却没有绑定
test_scopt()

Python中的模块代码在执行之前,并不会经过预编译,但是模块内的函数体代码在运行前会经过预编译,因此不管变量名的绑定发生在作用域的那个位置,都能被编译器知道。Python虽然是一个静态作用域语言,但变量名查找是动态发生的,直到在程序运行时,才会发现作用域方面的问题,

3. list,dict等复合变量里面的值都可以引用更改

def counter():
  n = [0]
  def compute():
    n[0] += 1 # 更改的是n里面的第一个值,不是更改n
    return n[0]
  return compute

func = counter()
func() # 1
func() # 2
func() # 3

4. global 声明全局变量,如果在局部要对全局变量修改,需要在局部也要先声明该全局变量

def counter1():
  n = 0
  def compute():
    global n # 如果在局部要对全局变量修改,需要在局部也要先声明该全局变量,但此处也会报错,因为没有全局变量n
    n += 1
    return n
  return compute


# right
def counter1():
  global n
  n = 0
  def compute():
    global n
    n += 1
    return n
  return compute

5. nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量

def make_counter():
  count = 0
  def counter():
    nonlocal count # 使用外层非全局变量
    count += 1
    return count
  return counter

作用域的类型

在Python中,使用一个变量时并不严格要求需要预先声明它,但是在真正使用它之前,它必须被绑定到某个内存对象(被定义、赋值);这种变量名的绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量。

L(local)局部作用域

局部变量:包含在def关键字定义的语句块中,即在函数中定义的变量。每当函数被调用时都会创建一个新的局部作用域。Python中也有递归,即自己调用自己,每次调用都会创建一个新的局部命名空间。在函数内部的变量声明,除非特别的声明为全局变量,否则均默认为局部变量。有些情况需要在函数内部定义全局变量,这时可以使用global关键字来声明变量的作用域为全局。局部变量域就像一个 栈,仅仅是暂时的存在,依赖创建该局部作用域的函数是否处于活动的状态。所以,一般建议尽量少定义全局变量,因为全局变量在模块文件运行的过程中会一直存在,占用内存空间。
注意:如果需要在函数内部对全局变量赋值,需要在函数内部通过global语句声明该变量为全局变量。

E(enclosing)嵌套作用域

E也包含在def关键字中,E和L是相对的,E相对于更上层的函数而言也是L。与L的区别在于,对一个函数而言,L是定义在此函数内部的局部作用域,而E是定义在此函数的上一层父级函数的局部作用域。主要是为了实现Python的闭包,而增加的实现。

G(global)全局作用域

即在模块层次中定义的变量,每一个模块都是一个全局作用域。也就是说,在模块文件顶层声明的变量具有全局作用域,从外部开来,模块的全局变量就是一个模块对象的属性。
注意:全局作用域的作用范围仅限于单个模块文件内

B(built-in)内置作用域

系统内固定模块里定义的变量,如预定义在builtin 模块内的变量。

作用域链:变量名解析LEGB法则

搜索变量名的优先级:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域
LEGB法则: 当在函数中使用未确定的变量名时,Python会按照优先级依次搜索4个作用域,以此来确定该变量名的意义。首先搜索局部作用域(L),之后是上一层嵌套结构中def或lambda函数的嵌套作用域(E),之后是全局作用域(G),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,则会出发NameError错误。

example 1

name = "lzl"

def f1():
  print(name)

def f2():
  name = "eric"
  f1()
  
f2() # 在函数未执行之前,作用域链就已经形成了,此时f1()的上一级应该name = 'lzl'

example 2

def scope_test():
  def do_local():
    spam = "local spam" # 此函数定义了另外的一个spam字符串变量,并且生命周期只在此函数内。此处的spam和外层的spam是两个变量,如果写出spam = spam + “local spam” 会报错
  def do_nonlocal():
    nonlocal spam  # 使用外层的spam变量 test spam
    spam = "nonlocal spam"
  def do_global():
    global spam
    spam = "global spam"
  spam = "test spam"
  do_local()
  print("After local assignmanent:", spam) # test spam
  do_nonlocal()
  print("After nonlocal assignment:",spam) # nonlocal spam
  do_global()
  print("After global assignment:",spam) # nonlocal spam ???? 先找是本地变量,找到的本地变量已经在do_nonlocal()里面改变了所以输出的是nonlocal spam

scope_test()
print("In global scope:",spam) # global spam

以上就是深入了解Python 变量作用域的详细内容,更多关于Python 变量作用域的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
两个使用Python脚本操作文件的小示例分享
Aug 27 Python
Python字典简介以及用法详解
Nov 15 Python
python实现守护进程、守护线程、守护非守护并行
May 05 Python
Python爬取qq空间说说的实例代码
Aug 17 Python
Python实现制度转换(货币,温度,长度)
Jul 14 Python
python中设置超时跳过,超时退出的方式
Dec 13 Python
Python通过VGG16模型实现图像风格转换操作详解
Jan 16 Python
python不同系统中打开方法
Jun 23 Python
解决Keras TensorFlow 混编中 trainable=False设置无效问题
Jun 28 Python
如何把python项目部署到linux服务器
Aug 26 Python
python 对图片进行简单的处理
Jun 23 Python
python读取mnist数据集方法案例详解
Sep 04 Python
详解Python IO编程
Jul 24 #Python
Python如何实现大型数组运算(使用NumPy)
Jul 24 #Python
基于opencv的selenium滑动验证码的实现
Jul 24 #Python
详解python中GPU版本的opencv常用方法介绍
Jul 24 #Python
python定义类的简单用法
Jul 24 #Python
Python爬虫抓取指定网页图片代码实例
Jul 24 #Python
详解Flask前后端分离项目案例
Jul 24 #Python
You might like
PHPExcel内存泄漏问题解决方法
2015/01/23 PHP
PHPExcel简单读取excel文件示例
2016/05/26 PHP
学习YUI.Ext 第七天--关于View&JSONView
2007/03/10 Javascript
Prototype 学习 工具函数学习($w,$F方法)
2009/07/12 Javascript
JavaScript中的apply()方法和call()方法使用介绍
2012/07/25 Javascript
js函数名与form表单元素同名冲突的问题
2014/03/07 Javascript
详解JavaScript ES6中的Generator
2015/07/28 Javascript
JavaScript实现上下浮动的窗口效果代码
2015/10/12 Javascript
js实现选项卡内容切换以及折叠和展开效果【推荐】
2017/01/08 Javascript
详解Angularjs在控制器(controller.js)中使用过滤器($filter)格式化日期/时间实例
2017/02/17 Javascript
angular2+nodejs实现图片上传功能
2017/03/27 NodeJs
js canvas实现擦除效果示例代码
2017/04/26 Javascript
详解vuex 中的 state 在组件中如何监听
2017/05/23 Javascript
AngularJS实现表单元素值绑定操作示例
2017/10/11 Javascript
Node使用Sequlize连接Mysql报错:Access denied for user ‘xxx’@‘localhost’
2018/01/03 Javascript
基于 Vue.js 2.0 酷炫自适应背景视频登录页面实现方式
2018/01/17 Javascript
vue组件中的数据传递方法
2018/05/14 Javascript
node.js到底要不要加分号浅析
2018/07/11 Javascript
命令行批量截图Node脚本示例代码
2019/01/25 Javascript
JS表单验证插件之数据与逻辑分离操作实例分析【策略模式】
2020/05/01 Javascript
JavaScript原生数组函数实例汇总
2020/10/14 Javascript
[01:21]DOTA2新纪元-7.0新版本即将开启!
2016/12/11 DOTA
使用python获取CPU和内存信息的思路与实现(linux系统)
2014/01/03 Python
Python实现从脚本里运行scrapy的方法
2015/04/07 Python
在Python中使用dict和set方法的教程
2015/04/27 Python
详解从Django Rest Framework响应中删除空字段
2019/01/11 Python
python实现智能语音天气预报
2019/12/02 Python
Pycharm连接远程服务器过程图解
2020/04/30 Python
Python中如何引入第三方模块
2020/05/27 Python
CSS3系列之3D制作方法案例
2017/08/14 HTML / CSS
The North Face意大利官网:服装、背包和鞋子
2020/06/17 全球购物
汽车技术服务英文求职信范文
2014/01/02 职场文书
2014年语文教师工作总结
2014/12/18 职场文书
导游词之澳门妈祖庙
2019/12/19 职场文书
Mysql binlog日志文件过大的解决
2021/10/05 MySQL
SQL Server中T-SQL标识符介绍与无排序生成序号的方法
2022/05/25 SQL Server