Python变量作用域

变量作用域是Python编程中非常重要的基础概念,理解它可以帮助你避免很多常见的错误。本文将用简单易懂的方式,带你全面掌握Python变量作用域的所有细节。

一、什么是变量作用域?

变量作用域(Scope)指的是变量在程序中的可见范围,也就是在程序的哪些地方可以访问这个变量。Python中有4种作用域,按照从内向外的顺序分别是:

  1. 局部作用域(Local) - 在函数内部定义的变量

  2. 嵌套作用域(Enclosing) - 在嵌套函数的外层函数中定义的变量

  3. 全局作用域(Global) - 在模块(文件)顶层定义的变量

  4. 内置作用域(Built-in) - Python内置的变量(如print、int等)

二、局部作用域(Local Scope)

在函数内部定义的变量属于局部作用域,只能在函数内部访问。

def my_function():local_var = "我是局部变量"print(local_var)  # 可以访问my_function()
print(local_var)  # 报错:NameError: name 'local_var' is not defined

特点

  • 函数调用时创建,函数结束时销毁

  • 不同函数中可以定义同名局部变量,互不影响

三、全局作用域(Global Scope)

在函数外部定义的变量属于全局作用域,可以在整个模块中访问。

global_var = "我是全局变量"def func1():print(global_var)  # 可以访问def func2():print(global_var)  # 可以访问func1()
func2()
print(global_var)  # 可以访问

四、global关键字

如果要在函数内部修改全局变量,需要使用global关键字声明。

count = 0def increment():global count  # 声明使用全局变量count += 1increment()
print(count)  # 输出: 1

如果不使用global: 

count = 0def increment():count = 1  # 这实际上是创建了一个新的局部变量print("函数内:", count)increment()  # 输出: 函数内: 1
print("函数外:", count)  # 输出: 函数外: 0

五、嵌套作用域(Enclosing Scope)

在嵌套函数中,外层函数的变量对内层函数可见。

def outer():outer_var = "外层变量"def inner():print(outer_var)  # 可以访问外层变量inner()outer()

nonlocal关键字

如果要修改嵌套作用域中的变量,需要使用nonlocal关键字。

def outer():x = 1def inner():nonlocal x  # 声明使用外层变量x = 2print("inner:", x)inner()print("outer:", x)outer()
"""
输出:
inner: 2
outer: 2
"""

六、作用域查找规则:LEGB

Python查找变量时按照LEGB规则依次查找:

  1. Local - 局部作用域

  2. Enclosing - 嵌套作用域

  3. Global - 全局作用域

  4. Built-in - 内置作用域

如果都找不到,就会抛出NameError异常。

x = "global"def outer():x = "enclosing"def inner():x = "local"print(x)  # 输出: localinner()outer()

 变量查找链(LEGB):
inner()调用时print(x)的查找过程:
1. 先在inner的局部作用域找 → 找到"local"(停止查找)
   ↑
2. 如果没找到,会去outer的作用域找 → "enclosing"
   ↑
3. 如果还没找到,会去全局作用域找 → "global"
   ↑
4. 最后会去内置作用域找

如果删除inner中的x赋值

修改代码:

x = "global"def outer():x = "enclosing"def inner():# 删除了x = "local"print(x)  # 现在会输出什么?inner()outer()

执行过程:

  1. inner()中print(x)查找x:

    • Local:未找到
      → 向上查找Enclosing作用域(outer的局部作用域)

  2. 找到outer中的x = "enclosing"

    • 输出: enclosing

七、常见作用域陷阱

1. 在循环/条件语句中没有独立作用域

Python中只有函数、类和模块会创建新的作用域,循环和条件语句不会。

if True:var_in_if = "if中的变量"print(var_in_if)  # 可以访问,输出: if中的变量for i in range(1):var_in_for = "for中的变量"print(var_in_for)  # 可以访问,输出: for中的变量

2. 列表推导式中的变量泄露

Python 3.x中列表推导式有自己独立的作用域,但Python 2.x中会泄露到外部作用域。

# Python 3.x
x = "hello"
[print(x) for x in range(3)]
print(x)  # 输出: hello(x没有被修改)# Python 2.x中x会被修改为2

3. 默认参数的作用域

默认参数在函数定义时求值,而不是在调用时。

def func(a, lst=[]):  # 默认列表在函数定义时创建lst.append(a)return lstprint(func(1))  # [1]
print(func(2))  # [1, 2] 使用的是同一个列表

具体过程:

  1. 当Python解释器读取到函数定义时,它会创建一个空列表对象作为默认参数

  2. 这个列表对象会被绑定到函数的__defaults__属性中

  3. 每次调用函数时,如果没有提供lst参数,就会使用这个同一个列表对象

实际执行过程

print(func(1))  # 第一次调用
"""
1. 没有提供lst参数,使用默认列表(假设内存地址为0x1000)
2. 向这个列表添加1 → [1]
3. 返回这个列表
输出: [1]
"""print(func(2))  # 第二次调用
"""
1. 仍然没有提供lst参数,使用同一个默认列表(还是0x1000)
2. 这个列表已经是[1]了,现在添加2 → [1, 2]
3. 返回这个列表
输出: [1, 2]
"""

为什么这是个问题?

  1. 不符合直觉:大多数人期望每次调用都使用一个新的空列表

  2. 隐藏的共享状态:函数调用之间意外共享了数据

  3. 难以调试:这种行为不明显,可能导致难以发现的bug

正确的做法

使用None作为默认值,然后在函数内部创建新列表:

def func(a, lst=None):if lst is None:lst = []lst.append(a)return lst

 现在每次调用都会得到预期行为:

print(func(1))  # [1]
print(func(2))  # [2] 这次是全新的列表

八、闭包和作用域

闭包(Closure)是函数记住并访问其词法作用域的能力,即使函数在其原始作用域之外执行。

def outer_func(x):def inner_func(y):return x + y  # inner_func记住了x的值return inner_funcclosure = outer_func(10)
print(closure(5))  # 输出: 15

九、实际应用建议

  1. 避免过多使用全局变量:会使代码难以维护和调试

  2. 合理使用函数封装:将相关代码和变量组织在函数中

  3. 注意变量命名:避免内外作用域同名变量引起混淆

  4. 使用nonlocal替代全局变量:当需要在嵌套函数中修改外层变量时

十、作用域相关面试题

  1. 下面代码的输出是什么?

x = 5def func():print(x)x = 10func()

答案:会报错,因为在函数中给x赋值,Python会认为x是局部变量,但在print时x还未定义

  1. 如何修改下面的代码使其正常工作?

total = 0def add_numbers(numbers):for num in numbers:total += numreturn totalprint(add_numbers([1, 2, 3]))

答案:需要在函数内使用global total声明

总结

理解Python变量作用域是写出高质量代码的基础。记住以下几点:

  1. 牢记LEGB查找顺序

  2. 修改作用域变量需要使用global或nonlocal

  3. 只有函数、类和模块会创建新作用域

  4. 避免滥用全局变量

  5. 合理使用闭包特性

希望这篇文章能帮助你彻底掌握Python变量作用域!如果有任何问题,欢迎在评论区留言讨论。

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

相关文章

初学者的AI智能体课程:构建AI智能体的十堂课

初学者的AI智能体课程:构建AI智能体的十堂课 在人工智能(AI)领域,AI智能体正在逐渐发挥其不容忽视的作用。自动化的智能体不仅仅在理论上广泛讨论,更加在实际应用中开辟了一片新的天地。那么如何动手开发属于自己的AI智能体呢?Microsoft提供的AI智能体入门课正是为此而设…

【并发编程】MySQL锁及单机锁实现

目录 一、MySQL锁机制 1.1 按锁粒度划分 1.2 按锁功能划分 1.3 InnoDB锁实现机制 (1)记录锁(Record Lock) (2) 间隙锁(Gap Lock) (3) 临键锁(Next-Key Lock) (4) 插入意向锁(Insert Intention Lock) 二、基于 JVM 本地锁实现,保证线程安全 2.1 线程不安全的分析 2.1…

能耗优化新引擎:EIOT平台助力企业降本增效

安科瑞顾强 数字化转型的背景下,能源管理正加速向智能化、远程化方向演进。安科瑞电气推出的EIOT托管平台及ADW300系列4G无线计量仪表,通过云端技术与无线通信的深度融合,为用户打造了高效、便捷的远程能源监测与管理体系,助力企…

(14)Element Plus项目综合案例

本系列教程目录:Vue3Element Plus全套学习笔记-目录大纲 文章目录 第3章 综合案例3.1 搭建项目3.1.1 创建Vite工程3.1.2 配置路由 3.2 登录模块页面3.2.1 注册页面3.2.2 登录页面3.2.3 忘记密码页面 3.3 导航设置3.3.1 头部3.3.2 侧边栏与底部1)头像部分…

Webug4.0靶场通关笔记22- 第27关文件包含

目录 一、文件包含 1、原理分析 2、文件包含函数 (1)include( ) (2)include_once( ) (3)require( ) (4)require_once( ) 二、第27关渗透实战 1、打开靶场 2、源码分析 3、…

〖 Linux 〗解决 VS Code 远程连接服务器的常见问题

文章目录 解决 VS Code 远程连接服务器的断开问题VS Code Remote-SSH一直弹出输入密码的问题VsCode C 语法检测失效不标红色波浪线 解决办法卸载扩展方式: 解决vscode C智能提示缓慢 解决 VS Code 远程连接服务器的断开问题 解决 vscode 卡顿,卡死&…

ERC-20与ERC-721:区块链代币标准的双星解析

一、代币标准的诞生背景 在以太坊生态中,代币标准是构建去中心化应用(DApps)的基石。ERC-20与ERC-721分别代表同质化与非同质化代币的两大核心标准,前者支撑着90%以上的加密资产流通,后者则开启了数字资产唯一性的新时…

C++入门小馆 :多态

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…

【NextPilot日志移植】整体功能概要

整体日志系统的实现功能 该日志系统主要实现了飞行日志的记录功能,支持多种日志记录模式,可将日志存储到文件或通过 MAVLink 协议传输,同时具备日志加密、空间管理、事件记录等功能。具体如下: 日志记录模式:支持按武…

数字化转型:概念性名词浅谈(第二十五讲)

大家好,今天接着介绍数字化转型的概念性名词系列。 (1)SOP(标准作业程序) 标准作业程序(Standard Operating Procedure, SOPs)是在有限时间与资源内,为了执行复杂的日常事务所设计的内部程序。从管理学的…

交叉编译 opencv-4.10

编译说明 opencv 下包含很多模块,各个模块的作用可以参考Opencv—模块概览. 嵌入式考虑有限存储等因素会对模块进行裁剪,我这里主要保留图像拼接(stitching)图片编解码(imgcodecs)与特征点匹配&#xff08…

Python cv2对象检测与跟踪:从基础到进阶实战

在计算机视觉领域,对象检测(定位目标位置)与对象跟踪(持续追踪目标运动)是视频分析、自动驾驶、智能监控等应用的核心技术。本文将结合OpenCV的cv2库,系统讲解其原理与Python实现方法。 一、对象检测 vs 对…

亚马逊推出新型仓储机器人 Vulcan:具备“触觉”但不会取代人类工人

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

缓存套餐-03.功能测试

一.功能测试 点击小程序,就会触发根据分类id查询套餐方法,根据分类id查询套餐。 第一次查询,redis中没有数据,就会发sql进行sql数据库查询。 redis当中就有了对应的缓存。 再次点击,发现sql根本没有执行,…

WebFlux与HttpStreamable关系解析

1-Streamable 1-WebFlux与HttpStreamable关系解析2-MCP协议Streamable HTTP 2-参考网址 MCP协议Streamable HTTPMCP协议重大升级,Spring AI Alibaba联合Higress发布业界首个Streamable HTTP实现方案 3-WebFlux与HttpStreamable关系解析 WebFlux 和 HttpStreamabl…

顺丰科技:从 Presto 到 Doris 湖仓构架升级,提速 3 倍,降本 48%

导读:顺丰科技引入 Doris 替换 Presto,在内部可视化数据自助分析工具丰景台场景广泛应用。目前,顺丰临时查询业务、丰景台报表业务的 Presto 场景已经 100% 切换到 Doris 集群中,日均查询量 100W。并实现 P95 性能提升近 3 倍&…

如何在Jmeter中调用C程序?

在JMeter中调用C语言程序可以通过以下几种方式实现: 方法一:使用OS Process Sampler JMeter的“OS Process Sampler”可以用来调用外部程序,包括C语言编写的可执行文件。 步骤: 准备C语言程序: 编写C语言代码并编译…

python 中的单例

在 Python 里,单例模式指的是一个类仅有一个实例,并且提供一个全局访问点来获取该实例。下面为你介绍几种实现单例模式的常见方法。 1. 使用模块 在 Python 里,模块天然就是单例模式。当模块被导入时,Python 会对其进行一次加载…

Linux58 ssh服务配置 jumpserver 测试双网卡 为何不能ping通ip地址

判断为NAT模式网卡 能ping 通外网 ens34为仅主机模式网卡 [rootlocalhost network-scripts]# ip route show default default via 10.1.1.254 dev ens33 proto static metric 100 10.0.0.0/8 dev ens33 proto kernel scope link src 10.1.1.37 metric 100 11.0.0.0/8 dev…

web 自动化之 selenium+webdriver 环境搭建及原理讲解

文章目录 一、web 自动化测试学习说明二、什么 web 自动化测试三、selenium 简介四、web自动化测试环境搭建五、web 自动化测试第一个脚本六、selenium 原理及源码讲解 一、web 自动化测试学习说明 进阶 web 自动化测试学习:掌握 python 编程基础 二、什么 web 自…