27. 作用域

news2025/1/15 16:37:42

1. 定义

作用域就是一个 python 程序可以直接访问命名空间的正文区域。
在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。

2. 四种作用域

2.1 局部作用域(Local,简称L)
  它的范围是一个函数/方法的内部。函数的参数,函数内定义的各种变量或对象都属于局部作用域`。
搜索变量时首先被搜索。这是最内层的作用域。

# 函数参数num1和num2, 以及value是局部变量, 属于局部作用域
# 在getSum()的整个函数体内都可以被直接访问
def getSum(num1, num2):
    value = num1 + num2
    return value

2.2 嵌套作用域(Enclosing,简称E)
  主要用在有嵌套函数的场景,用来实现闭包包含了非局部(non-local)也非全局(non-global)的变量
比如两个嵌套函数,一个函数A 里面包含一个函数B ,如果在函数B中访问了函数A中的局部变量,那么在函数B就会创建一个__closure__属性,用于保存A中这些被B访问的局部变量。这个__closure__属性中的变量的作用域就是嵌套作用域。

# outer()是外部函数, inner()是内部函数.
# 变量a是outer()中的局部变量, 是局部作用域, outer函数体中的任何位置都可以直接访问变量a;
# add函数也是一个特殊的outer函数中的局部变量;
# inner()是一个内部函数, 访问了外部函数outer()中的局部变量a, 因此外层函数outer()中的变量a被放入inner()的_closure__属性中;
# a就属于嵌套作用域, 在嵌套函数inner()的任何位置都可以直接访问变量a.
def outer():
    a = 100

    def inner(b):
        return a + b

    return inner

my_add = outer()
print(my_add(10))
print(my_add(20))

2.3 全局作用域(Global,简称G)
它的范围是当前模块的任何位置。模块中的顶层区域导入的其它模块、定义的全局变量、函数和类,都属于全局作用域。

# 在模块的顶层区域定义的都是全局变量, g1是属于全局作用域, 在本模块的任何位置都可以被访问;
# test也是一个特殊的全局变量, 变量名是test, 属于全局作用域, 在本模块的任何位置都可以被访问;

g = 100

def test():
    print("访问全局变量g={}".format(g))  # 访问全局变量g

# 函数调用(访问全局变量test)
test()

2.4 内置作用域(Built-in,简称B)
它的范围是所有模块的任何位置。python内置的数据类型、内置函数、标准异常等都属于内置作用域。

# max是内置函数, 它的作用域类型是Builtin作用域;
# 在模块的任何位置都可以访问它; 
def test():
    print(max(1, 2))

print(max(1, 2))

3. 变量的查找顺序

规则顺序: L –> E –> G –> B
(1) 先在局部作用域找;
(2) 找不到,就去嵌套作用域找(例如存在闭包的场景);
(3) 再找不到,就会去全局作用域找;
(4) 再找不到,就去内置作用域中。如果还是没找到,会抛出NameError的异常。

上述的4个过程中,任何一个找到,就会停止查找,使用找到的这个变量的值。注意:变量查找时,LGB三个作用域是一定存在的,E作用域不一定存在。

4. 作用域和命名空间的关系

作用域是建立在命名空间之上的一个虚拟的概念,所有的变量都是存储在某个命名空间里面的,作用域决定了这些变量的可访问范围(可见性)。当命名空间被创建后,其对应的作用域就被确定(或是形成)。当一个变量被定义后,就会被加入到该代码行所在的命名空间里,这时候,该命名空间对应的作用域范围的任何位置,都可以访问该变量。

5. 哪些代码能生成命名空间和命名作用域

python 中只有模块(module),类(class)、函数(def、lambda)以及列表推导才会创建新的命名空间并形成新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会创建新的命名空间以及形成新的作用域也就是说这些语句内定义的变量,外部也可以访问

# 1.实例中text变量定义在if语句块中, 但外部还是可以访问的.
if True:
    text = "HelloWorld."

print(text)

# 2.如果将text定义在函数中, 则它就是局部变量, 外部不能访问.
# 企图在外部访问函数内部定义的变量, 错误. fNameError: name 'count' is not defined
def test():
    count = 2

print(count)

6. global 和 nonlocal关键字

6.1 global关键字
  要想在局部作用域中修改全局变量的值,必须使用global关键字对变量进行修饰,才能修改成功

# [demo1]
# 以下代码企图用来修改全局变量g_count的值, 修改失败.
# fun1()中的g_count是局部变量, 修改它, 并不会对全局变量g_count造成任何影响;
# 所以最终输出结果是:g_count:>  99
g_count = 99

def fun1():
    g_count = 66

fun1()

print("g_count:> ", g_count)  
# [demo2] 
# 通过global关键字进行修饰, 修改成功.
# 程序运行结果:g_count:>  100
g_count = 99

# 要想修改全局变量g_count, 则需要使用global关键字
def fun1():
    global g_count
    g_count = 100

fun1()

print("g_count:> ", g_count)

6.2 nonlocal关键字
  在嵌套函数中修改嵌套作用域中的变量(也就是修改外层函数的局部作用域中的变量),必须使用 nonlocal 关键字对变量进行声明

# [demo1]
# 企图在inner()中修改外部函数中的局部变量number的值, 修改失败.
# 输出结果是: number is:>  10
def outer():
    number = 10

    def inner():
        number = 10000

    inner()
    print("number is:> ", number)

outer()
# [demo2]
# 通过关键字nonlocal进行修饰, 则修改成功.
# 输出结果是: number is:>  10
def outer():
    number = 10

    def inner():
        nonlocal number
        number = 10000

    inner()
    print("number is:> ", number)


outer()

6.3 分析
上面的2个例子(5.1和5.2),在不加相应的关键字的情况下,都没能如期修改外部作用域中的变量的值。那么,没加关键字的情况下,肯定是生成了新的变量,导致赋值给了这个新的变量。这个新变量是当前作用域中的局部变量。我们可以通过locals()来加以验证。
globals():以字典形式返回当前位置可以访问的所有的全局变量的信息。
locals():以字典形式返回当前位置可以访问的所有的局部变量的信息。

(1) 修改6.1中的[demo1]

# 通过打印发现, fun1()中的g_count确实是一个局部变量.
g_count = 99

def fun1():
    g_count = 66
    print(locals())

fun1()

print("g_count:> ", g_count) 

在这里插入图片描述

(2) 修改6.2中的[demo1]

# 通过打印发现, inner()中的number确实是一个局不变量.
def outer():
    number = 10

    def inner():
        number = 10000
        print(locals())

    inner()
    print("number is:> ", number)

outer()

在这里插入图片描述

6.4 特殊情况

# 执行以下代码, 将发生报错;
# 错误信息为局部作用域引用错误, 因为test函数中的a使用的是局部, 未定义, 无法修改.
a = 10

def test():
    a = a + 1
    print(a)

test()

在这里插入图片描述
那么该如何修改呢?
(1) 使用global进行修饰

# 输出:11
a = 10

def test():
    global a	# global
    a = a + 1
    print(a)

test()

(2) 作为函数参数传递

# 输出:11
a = 10

def test(a):
    a = a + 1
    print(a)

test(a)

7. 其他一些情况

7.1 在局部作用域中导入(import)模块

def test():
    # 此时sys属于test中的局部作用域
    import sys
    print(sys.path)


test()

# 全局作用域不能访问局部作用域中的变量
print(sys.path)

在这里插入图片描述
7.2 变量访问出现UnboundLocalError
  对于这种情况,python会抛出UnboundLocalError异常。对于print(a)来说,python会认为是访问这个局部变量a而不是外面的全局变量a,但这个局部变量还没有被定义,不能被访问。所以就抛出这个异常。

def test():
    print(a)  # 会抛出UnboundLocalError异常
    a = 100

a = 1
test()

在这里插入图片描述
7.3 访问列表推导式中的变量出现NameError异常

def test():
    ls = [i for i in range(6)]
    print(i)  # 抛出NameError异常


test()

在这里插入图片描述
  因为列表推导式会创建自己的命名空间并形成自己的作用域,因此变量i并不属于外层的test()中的局部作用域,在进行变量i的查找时,不会在内层作用域进行查找。
7.4 函数调用时

a = 1  # 全局变量a

def test1():
    print(a)

def test2():
    # 创建一个新的局部变量a, 属于test2的局部作用域
    a = 200

    # 在这里只是调用函数test1, test1中的局部作用域和这里的test2的局部作用域没有任何关系
    # 也不存在嵌套函数的关系, 所以test1中的a的输出值是全局作用域中的a的值1,而不是这里的a=200
    test1()

test2()

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/178644.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Hadoop】MapReduce原理剖析(Map,Shuffle,Reduce三阶段)

文章目录1. Map阶段1.1 把输入文件(夹)划分为很多InputSplit(Split)1.2 分配并执行map作业2. Shuffle阶段2.1 Partition(分区)2.2 Sort(排序)2.3 Group(分组)2.4 Combiner(规约)2.5 序列化并写入Linux磁盘内存2.6 反序列化读取数据到不同的reduce节点2.7 Reduce端数据进行合并、…

【数据库概论】第五章 数据库完整性

第五章 数据库完整性 目录第五章 数据库完整性5.1 实体完整性5.2 参照实体性5.3 用户定义的完整性1.属性上的约束条件2.元组上的约束条件5.4 完整性约束命名子句5.5 域中的完整性限制5.6 断言5.7 触发器(Trigger)一、定义触发器二、激活触发器三、删除触发器数据库的完整性指的…

好客租房-13.WebMagic

13. 项目接入ES编写爬虫抓取房源数据开发搜索房源接口服务整合前端开发实现搜索功能优化搜索功能增加高亮和分页功能热词推荐功能实现拼音分词13.1 制作假数据13.1.1 WebMagic抓取数据为了丰富我们的房源数据,所以我们采用WebMagic来抓取一些数据,目标网…

还在纠结选择用什么浏览器?手机端用国产浏览器也很香

一说到受欢迎的电脑浏览器,大家肯定不约而同地说谷歌浏览器。微软edge浏览器能够同步书签、插件也非常多,因为这些优势深受国人的喜爱。有人纠结在国内选择谷歌好,还是edge浏览器好呢?可能有的人哪个也不选,反而在电脑…

Docker 解决 `denied: requested access to the resource is denied`

背景 由于不可描述的原因,相对于以前,最近在更加频繁的迁移服务器,简单的 Shell 脚本已经不能满足需求了,于是将所有的项目 Docker 化。 部分不含敏感配置的项目准备放到 DockerHub 上面,但是在 docker push 的时候报…

利用 Algolia 为静态博客搭建实现内容搜索

现在静态博客的标配之一就是博客搜索🔍,我也是通过搭建博客发现了它,这篇主要记录一下怎么使用 algolia 完成博客搜索,自己的博客搭建使用的是 docusaurus 。 注册账号 首先需要去 algolia 官网注册自己的账号,可以直…

Java线程池(超详细)

1、基本概念 Java线程需要经过线程的创建,调用和销毁整个过程,频繁的创建和销毁会大大影响性能,所以引入的线程池: 好处: 提升性能:线程池能独立负责线程的创建、维护和分配线程管理:每个Java…

k8s安装kuboard面板

前面介绍了k8s的dashboard面板,这里介绍国人开发的kuboard面板,相较于dashboard面板,kuboard面板对很多运维调试功能做了很多增强。官方文档:https://www.kuboard.cn/install/v3/install.html#kuboard-v3-x-%E7%89%88%E6%9C%AC%E8…

实现一个TCP客户端——服务端协议

目录 TCP客户端常见的API: ServerSocket: Socket: TCP服务端(单线程版本) 属性构造方法: 启动服务端的start()方法 步骤一:接收客户端发送的socket 步骤二: 调用processConnection方法来处理客户端发送的连接 ①通过参数传入的…

影像组学|特征定义以及提取

一、 影像组学特征分类 1.1 影像组学特征分类 1.1.1 一阶统计特征 一阶统计特征,反应所测体素的对称性、均匀性以及局部强度分布变化。包括中值,平均值,最小值,最大值,标准差,偏度,峰度等。 …

【Linux】六、Linux 基础IO(三)|文件系统|软硬链接|文件的三个时间

目录 八、文件系统 8.1 磁盘 8.1.1 磁盘的物理结构 8.1.2 磁盘的存储结构 8.1.3 磁盘的逻辑结构 8.2 inode 九、软硬链接 9.1 软链接 9.2 硬链接 9.3 当前路径(.)和上级路径(..) 十、文件的三个时间 八、文件系统 上面的内容谈论的都是一个被打开文件,那…

如何将两个录音合成一个?这篇文章告诉你

现如今,很多小伙伴都加入到短视频行业当中。而短视频的制作往往需要将多段音频进行一个合并。那么问题来了,当你想多个音频进行合并在一起的时候,你是怎么做的呢?其实很简单,我们只需要借助市面上的一些合并软件就好了…

初始网络

文章目录初始网络局域网 / 广域网IP地址 和 端口号认识协议协议分层初始网络 这里可以先自行在网上了解一下网络的发展史 也就是互联网是怎么来的. 局域网 / 广域网 关于网络的发展史 , 会涉及到两个非常重要的术语 ,也就是 局域网,和广域网 。 局域网 &…

JavaEE多线程-阻塞队列

目录一、认识阻塞队列1.1 什么是阻塞队列?1.2 生产者消费者模型1.3 标准库中的阻塞队列类二、循环队列实现简单阻塞队列2.1 实现循环队列2.2 阻塞队列实现一、认识阻塞队列 1.1 什么是阻塞队列? 阻塞队列:从名字可以看出,他也是…

简明Java讲义 2:数据类型和运算符

目录 1、安装IDE编辑器 2、关键字和保留字 3、标识符 4、分隔符 5、数据类型 6、基本类型的数据类型转换 7、表达式类型的自动提升 8、变量 9、运算符 10、运算符的优先级 1、安装IDE编辑器 在开始内容之前,先下载IDE,可以是Eclipse或STS&…

Python函数(函数定义、函数调用)用法详解

Python 中函数的应用非常广泛,前面章节中我们已经接触过多个函数,比如 input() 、print()、range()、len() 函数等等,这些都是 Python 的内置函数,可以直接使用。除了可以直接使用的内置函数外,Python 还支持自定义函数…

LeetCode刷题模版:201 - 210

目录 简介201. 数字范围按位与202. 快乐数203. 移除链表元素204. 计数质数205. 同构字符串206. 反转链表207. 课程表【未实现】208. 实现 Trie (前缀树)209. 长度最小的子数组210. 课程表 II【未实现】结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您…

LeetCode[1319]连通网络的操作次数

难度:中等题目:用以太网线缆将 n台计算机连接成一个网络,计算机的编号从 0到 n-1。线缆用 connections表示,其中 connections[i] [a, b]连接了计算机 a和 b。网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其…

(十六)异步编程

CompletableFuture在Java8中推出,Java8中的异步编程就是依靠此类。几种任务接口四种任务无参数有一个参数有两个参数无返回值RunnableConsumerBiConsumer有返回值SupplierFunctionBiFunctionCompletionStage接口这个类中定义的许多能够链式调用的方法和组合方法时是…

Unity3DVR开发—— XRInteractionToolkit(PicoNeo3)

目录 一、开发前的准备 二、基础配置 三、Pico项目配置 四、添加基础功能 一、开发前的准备 1、为了方便开发,先在Pico开发者平台里下载预览工具 Pico开发者平台https://developer-global.pico-interactive.com/sdk?deviceId1&platformId1&itemId17 2、…