49、Python之模块和包:模块导入对命名空间的影响

news2024/11/15 13:25:29

引言

前面文章中,关于Python解释器在模块导入行为背后所执行的操作,已经做了深入的介绍。本文打算在此基础上,结合实际代码案例,进行进一步的补充说明。同时,比较看似只是微小的导入方式的改变,可能会导致的难以理解的异常问题。

本文的主要内容有:

1、关于命名空间

2、模块导入对命名空间的影响

关于命名空间

有关于命名空间的介绍,前面已经很详细了,对此仍然不太熟悉的,可以翻看一下上一篇文章。这里,主要想就在Python中如何查看、操作命名空间做进一步的说明。

命名空间的作用,主要是用于进行代码中“名字”与对象之间绑定关系的存储。

所谓执行模型,更像是代码在命名空间上执行,从而实现对Python中对象模型的访问及修改。

所以,代码执行过程中,对名称的解析、对象的定位,都是基于命名空间来实现的。

在Python中我们可以通过几个内置函数查看命名空间的相关内容,前面已经简单提及,这里再回顾一下:

1、globals():查看当前全局命名空间的字典

2、locals():查看当前局部命名空间的字典

3、dir():返回当前代码所在范围的所有名字列表

可以先看下这几个内置函数的文档说明:

607a8868f3000a8945835e2f92c4152e.jpeg

7d9c7346c271635629be590aec0d2506.jpeg

da2be96b415dda1dd85f56e035508f3e.jpeg

接下来,我们通过实际代码来看下,这几个内置函数的使用。

首先看代码执行前后全局命名空间的变化,直接看代码:

from rich.pretty import pprint
print("初始的全局命名空间:")
pprint(locals())
a = 10
b = 20


def add(n1, n2):
    return n1 + n2


print("定义了变量和函数后的全局命名空间:")
pprint(locals())

执行结果如下:

b62d35fe7a85dbc3512c3b193497df25.jpeg

从执行结果可以看到,我们在代码中定义的变量和函数,都会把名称和对应的对象存储到全局命名空间的字典中。

需要说明的是,当我们在顶级代码块中,使用locals()函数时,返回的命名空间与globals()返回的是一样的,也就是都是该模块的全局命名空间。

感兴趣的,可以自行把代码中的globals()切换为locals()。

当locals()函数在函数体内进行调用时,则能看到真实的函数内部的局部命名空间中的名称及对象的绑定关系:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    pprint(locals())
    return a + n1 + n2


pprint(globals())
add(10, 20)
c = add(5, 10)
pprint(globals())

执行结果:

1bc18de926de9169151f87660bc98157.jpeg

从执行结果中可以看出,虽然add()函数的局部命名空间中只有n1和n2两个名称,但是执行到return a + n1 + n2时,并不会报错,且成功返回了我们期望的10 + 5 + 10 = 25的结果。

所以,当函数调用时,会创建局部命名空间。函数体中的代码会在局部命名空间上执行,如果在名称解析的过程中,如果遇到局部命名空间中不存在的名称,则会去更高层级的命名空间去查找,查找顺序遵循LEGB规则。

如果我们稍微调整一下add()函数体的代码,尝试修改全局变量a的取值:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    a = 1000
    pprint(locals())
    return a + n1 + n2


pprint(globals())
add(10, 20)
add(5, 10)
pprint(globals())

执行结果:

0eec43441ab0fd349d2682c915ccf144.jpeg

从代码中可以看出,我在顶级代码块中定义了一个全局变量a,在函数add中尝试修改全局变量a,其实并没有成功。

因为add函数体中,a = 1000这行代码,默认是在局部命名空间中执行,所以会在局部命名空间中创建一个新的名称对象绑定关系。所以,对比前面一个程序的运行结果,局部命名空间中多了一个a局部变量的名称对象绑定。

虽然全局命名空间中也有同名的变量a,但是在函数的区局作用域中,会优先查找局部命名空间来进行名称解析。

现在陷入了一个尴尬的点,我们想要修改全局变量,却导致了在局部命名空间中新增了一个局部变量。

其实有两种做法可以实现在局部作用域(也就是函数体中),对全局变量的修改。

先来看比较粗暴的做法,既然我们的意图是修改全局变量,而全局变量的名称对象绑定是存储在全局命名空间中,那么我们直接尝试修改全局命名空间中的字典,是否就实现了对全局变量的修改呢?

可以通过如下代码来验证:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    globals()['a'] = 1000
    pprint(locals())
    return a + n1 + n2


pprint(globals())
add(10, 20)
c = add(5, 10)
pprint(globals())

我们将add()函数体中,a = 1000的代码,变换为:globals()['a'] = 1000。

看下执行结果:

488658db0ae08f981ea785a8a4764d55.jpeg

从执行结果看出,这种做法,确实直接修改了全局命名空间中a变量,而且没有在局部命名空间中,引入新的局部变量,从而实现了我们的目的。

我们也可以不通过定义变量,直接通过这种操作全局命名空间字典的方式,引入新的变量,感兴趣的同学,可以自行尝试。

之所以说,这种做法比较粗鲁,一方面是因为有点繁琐,另外一方面也不太安全,稍有不慎,会影响后续代码中的命名解析。

其实,Python中有更简单的做法,就是通过global关键字,来声明变量为全局变量,代码如下:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    print(dir())
    global a
    a = 1000
    pprint(locals())
    return a + n1 + n2


pprint(globals())
add(10, 20)
c = add(5, 10)
pprint(globals())

执行结果,跟粗鲁的做法是完全相同的,这里就不再贴出来了。

此外,dir()函数,不传参时,能直接获取当前作用域中可见的名称列表,这里就不再演示了。

模块导入对命名空间的影响

接下来看一下,模块导入会对命名空间产生怎样的影响。

首先,我们定义一个m1.py的模块,代码如下:

a = 100


def add(n1, n2):
    print("在m1的add函数中")
    print(locals())
    return a + n1 + n2


print("在m1模块中")
print(dir())

然后,我们在入口文件中导入该模块,代码如下:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    print("在入口文件的add函数中")
    pprint(locals())
    return a + n1 + n2


pprint(globals())
import m1

c = add(5, 10)
d = m1.add(20, 10)
m1.a = 1000
e = m1.add(20, 10)
pprint(globals())

执行结果如下:

70e7f70ca49b97de733f3e24b2073a47.jpeg

可以看到,通过import m1的方式导入模块,在全局命名空间中只增加了一个m1的模块名与模块对象的绑定关系。虽然我们在模块m1和入口文件中,有同名的全局变量a和add()函数,但是模块中的是通过m1.的前缀访问的。所以,可以各自访问,并不会有冲突或者覆盖的情况。

但是,如果导入方式调整为 from m1 import a, add

代码如下:

from rich.pretty import pprint

a = 10


def add(n1, n2):
    print("在入口文件的add函数中")
    pprint(locals())
    return a + n1 + n2


pprint(globals())
from m1 import a, add

d = add(20, 10)
a = 1000
e = add(20, 10)
pprint(globals())

执行结果:

a144c11fbf9233a66d8edcc02122ffb9.jpeg

从执行结果中,可以看出,通过from m1 import a, add的方式进行导入时,全局命名空间中原有的名称a和add的绑定关系发生了变化。也就是入口文件中定义的变量a和函数add()已经被覆盖。

同时会发现,不同于通过m1.a = 1000进行模块m1中全局变量的修改,这里m1中的变量a直接被加入到了入口文件的全局命名空间,所以,a = 1000,并没有影响函数add()的计算结果,因为没有真正修改add()函数中引用的m1.a变量的取值。这点,是需要格外注意的。

总结

本文首先介绍了命名空间相关的补充内容,然后演示了模块导入对命名空间的影响。需要注意的有这几点:

1、当在顶级代码块或者全局作用域中,globals()和locals()获取的命名空间字典都是相同的,都是该模块的全局命名空间。

2、在函数体内或者局部作用域中,globals()是全局命名空间,locals()则是真正的局部命名空间,只有函数的形参及函数体内定义的变量。

3、要在局部作用域中修改全局变量,可以通过globals()['变量名'] 的方式进行修改,也可以通过global关键字的方式进行修改,推进使用后一种方法。

4、import 模块名的方式,只会在全局命名空间中添加模块名与模块对象的名称绑定关系,所以,不会导致命名冲突或者相互覆盖的情况。

5、from 模块名 import xxx的导入方式,会导致模块中具体的变量名、函数名等添加到全局命名空间中,如果已经存在相同的名称,则会覆盖。最终的结果是,最后一次导入的真正生效,之前的都会被覆盖掉。此外,当模块中的函数需要访问模块中的全局变量时,此种导入方式,会导致无法修改模块全局变量的情况,需要特别注意!

感谢您的拨冗阅读,如果对您学习Python有所帮助,欢迎点赞、关注。

ac169efeca159192bf68361bd2c20e2c.jpeg

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

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

相关文章

「ComfyUI」生图修图神器,自定义调节颜色光暗,更生动更强对比度生图技巧分享!

前言 ‍‍‍‍‍前 言 今天再给小伙伴们分享一个简单又实用生图神器插件,可以调整整个图像的光暗变化以及颜色变化。 原理的话,我们也简单来说下,我们在使用 VAE 将图像编码为潜在噪声时,VAE 解码的值通常在一定范围内&#xf…

在Mac上打开UE4Editor

编译MacEditor 使用如下命令在Mac机器上编译Mac的UE4Editor: ${EnginePath}/Engine/Build/BatchFiles/Mac/Build.sh ${ProjectName}Editor Mac Development ${ProjectPath} -buildubt -buildscw -waitmutex -log${ClientPath}/Saved/Logs/${ProjectName}Editor.log…

SpringBoot集成kafka-自定义拦截器(可以在拦截器中做记录日志、安全检查等操作)

TOC 1、kafka配置类 kafka配置类添加Configuration注解,springboot启动后会自动读取该配置类;由于在application.yml文件中我们找不到kafak拦截器相关的配置项,因此需要自定义拦截器;消费者相关配置方法中添加自定义拦截器配置&a…

FORTIFY: FD_ISSET: file descriptor 1024 >= FD_SETSIZE 128 记录

问题 在开发过程中,遇到一个问题,即使用FD_ISSET时,当文件描述符数量超过1023,导致netd进程出现crash。通过代码和log分析,发现这是由于内核限制导致的数组越界问题。 总结:FD_ISSET(sock, &read_fds)…

【日记】这个月花了好多钱(1317 字)

正文 这几天都好热。热到人不想动,只想睡觉。 今天写文章发现自己有个很显著的特点,就是在有个框架之后,具体细节完全没有预设。我只能像马尔可夫链一样,形成一个比较窄的窗口,接着这个窗口里的情节往下写,…

.NET Razor类库 - 生成NuGet包

上一篇讲了Razor类库组件化:https://blog.csdn.net/CsethCRM/article/details/141558974 本篇说一下Razor类库生成NuGet包 1.右键Razor类库项目 - 属性 2. 输入Nuget 包信息 点击 左侧菜单 包 在生成操作期间 创建包文件 打勾 版本号 我们输入 2023.1.0 作者 Xxx…

外卖霸王餐项目是什么?怎么搭建属于自己的外卖霸王餐小程序 ?

前言: 外卖霸王餐项目是一种结合了优惠促销与推广合作的商业模式,主要针对外卖行业。这个项目的核心是通过提供低于市场价的外卖餐品(通常是半价或者更大折扣)来吸引新用户尝试,并通过用户的口碑传播来增加餐厅的知名…

降本高达30%,磁集成是电源企业的福音吗?

导语 为什么说磁集成将会是大功率电源产品趋势?因为终端价格战越来越激烈,只有磁集成才能同时解决电源企业的三大核心竞争需求。 终端持续“卷”价格 储能价格正式步入0.5元时代。从价格战的角度来看,储能领域自2023年起就已经进入“0.5元/Wh时代”&…

集群 NAT(地址转换)、TUN(IP隧道)、DR(直接路由)

一、企业群集应用概述 1、群集的含义 ①、Cluster、集群、群集 ②、由多台主机构成,但对外只表现为一个整体,只提供一个访问入口(域名与IP地址),相当于一台大型计算机。 2、问题 ①、互联网应用中,随着站…

composer常用命令列表和实践使用、服务器lnmp环境自动化部署脚本及netstat命令常用选项笔记-及state各值的意义

一、composer常用命令列表和实践使用 1. composer常用的命令列表如下: #. composer install 命令(composer.lock与composer.json) 如果当前目录下存在composer.lock文件,则从此文件读取依赖版本,否则就读取compose…

计算机毕业设计选题推荐-社区康养管理系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

SQL注入漏洞WAF绕过

目录 如何检测和防范SQL注入攻击中的编码伪装? 检测SQL注入攻击中的编码伪装 防范SQL注入攻击中的编码伪装 WAF在处理SQL注入时为什么有时会对大小写不敏感? SQL注入中的联合查询注入有哪些常见的攻击方式? 在绕过Web应用防火墙&#xf…

软件工程造价师习题练习 22

1.公文管理系统可以设置公文处理提示的方式和频率。系统缺省设置为邮件方式及每天提醒。则对于公文管理系统,“公文处理提示方式及频率的缺省设置信息”配置信息缺省默认值是业务数据。 正确 错误 要判断“公文处理提示方式及频率的缺省设置信息”配置信息缺省默认值是否是业务…

Nginx 反向代理实现 Tomcat 高可用性负载均衡详解

Tomcat 简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务…

vue项目关于ERR_OSSL_EVP_UNSUPPORTED的问题

opensslErrorStack: [ error:03000086:digital envelope routines::initialization error ], library: digital envelope routines, reason: unsupported, code: ERR_OSSL_EVP_UNSUPPORTED 该问题通常与 OpenSSL 库版本不兼容或配置问题有关,特别是在使用 No…

Docusign IAM|5 种方式优化团队的协议管理工作流!

本文将介绍 Docusign IAM 如何帮助你的团队自定义协议工作流程并改进端到端协议流程的五种方式。 团队创建、承诺和管理协议的传统方式充满漏洞。这些流程涉及过多的技术系统、繁复的步骤,以及员工在不同工具间手动转移文档和数据的繁琐操作。我们在与企业讨论其协议…

【运维高级内容--MySQL】

目录 一、mysql安装 二、MySQL主从复制 一、mysql安装 yum install cmake gcc-c openssl-devel ncurses-devel.x86_64 rpcgen.x86_64 #安装依赖性 #在root路径下下载mysql-boost-5.7.44、libtirpc-devel-1.3.3-8.el9_4.x86_64.rpm安装包 yum install libtirpc-devel…

基于深度学习的道路缺陷检测系统(含UI界面、yolov5、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下: 算法模型:     yolov5、yolov5 SE注意力机制,两个模型都已训练好,可直接使用。 数据集:     网上下载的数据集,格式都已转好,可…

下载官方llama

1.官网.pth格式 去官网(Download Llama (meta.com))申请 具体可以看这个B站视频 Llama2模型申请与本地部署详细教程_哔哩哔哩_bilibili(视频是llama2,下载llama3是另外一个git) 相关代码如下 git clone https://g…

【那些年错过的好书】——Python数据可视化:科技图表绘制

正文开始 前言推荐理由作者简介书籍介绍章节介绍实书示例写在最后 前言 读万卷书,行万里路。 书籍免费获取方式:小程序搜索【中二少年工具箱】,找到抽奖功能(如果已经做出来的话),直接抽奖获取。或者私信…