Python中通过字符串访问与修改局部变量

嗨喽~大家好呀,这里是魔王呐 ❤ ~!

python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取

在Python中定义一个函数时,就会把变量空间划分为全局变量(global)与局部变量(local),

如果是定义在一个类的成员函数中,那么就还有额外的成员变量(self)空间。

那么,如果在实际操作中,想把这几种不同的变量空间做一个分离的话,有没有办法呢?

读取和修改局部变量

首先来看一下局部变量的读取,一般有locals()、vars()和sys._getframe(0).f_code.co_varnames这几种方法,另外有一种sys._getframe(0).f_locals的方法,其实等价于locals(),

相关的实现代码如下:

x = 0class Obj:def __init__(self,y):self.func(y)def func(y, z=1):m = 2print (locals())print (vars())print (__import__('sys')._getframe(0).f_code.co_varnames)if __name__ == '__main__':Obj(2)

该代码的运行结果如下:

{'self': <__main__.Obj object at 0x7f5cf5e74e50>, 'y': 2, 'z': 1, 'm': 2}
{'self': <__main__.Obj object at 0x7f5cf5e74e50>, 'y': 2, 'z': 1, 'm': 2}('self', 'y', 'z', 'm')

在vars方法不加具体变量名的时候,就是等价于locals方法,两者返回的结果都是字典格式。

如果是一个类中的成员函数下执行locals或者vars,会附带一个__main__.Obj object的变量,相当于所有self的成员变量,其实也是局部变量的一部分。

而如果使用co_varnames的方法,那么得到的就是所有局部变量的名称,

我们也可以在例子中额外定义一个self的成员变量:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
x = 0class Obj:def __init__(self, y):self.p = 5self.func(y)def func(self, y, z=1):m = 2print(locals())print(vars())print(__import__('sys')._getframe(0).f_code.co_varnames)if __name__ == '__main__':Obj(2)# {'self': <__main__.Obj object at 0x7fe9aac0ce50>, 'y': 2, 'z': 1, 'm': 2}# {'self': <__main__.Obj object at 0x7fe9aac0ce50>, 'y': 2, 'z': 1, 'm': 2}# ('self', 'y', 'z', 'm')

可以发现,所有的成员变量都被放在了self中。

并且需要注意的是,全局变量x自始至终都没有在局部变量中出现。

那么既然我们可以通过这种方式分离出局部变量,或者是局部变量的名称,那我们如何去调整或者修改这些局部变量呢?

首先我们需要知道,locals()方法返回的变量是一个copy,也就是说即使修改了locals方法返回的结果,也不能真正的改变局部变量本身的值,这样描述可能有点抽象,我们直接看下这个案例:

x = 0class Obj:def __init__(self,y):self.func(y)def func(self, y, z=1):m = 2vars()['z']=2locals()['n']=3print (locals())print (z)if __name__ == '__main__':Obj(2)

在这个案例中分别通过vars方法和locals方法去修改局部变量的值,最终的输出结果如下:

{'self': <__main__.Obj object at 0x7f74d9470e50>, 'y': 2, 'z': 1, 'm': 2, 'n': 3}1

首先要解释一下为什么这个案例中没有打印n这个变量,前面提到vars和locals的返回值都是真实变量的一个copy,因此我们不管是修改也好,新增也好,内容不会同步到变量空间中去,也就是说,此时的局部变量n还是处于一个没有定义的状态,只是在locals或者vars的字典中存在,此时打印只会报错NameError。

而z的最终打印输出是1,这表明z的值确实没有受到对vars的变量修改的影响。

那到底有没有办法可以通过字符串去修改局部变量呢(不同步到全局变量)?

答案是有的,但是这个方案非常的hacky,请看如下示例:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import ctypesx = 0class Obj:def __init__(self,y):self.func(y)def func(self, y, z=1):m = 2__import__('sys')._getframe(0).f_locals.update({'z': 2,'n': 3})ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(__import__('sys')._getframe(0)), ctypes.c_int(0))print (locals())print (z)if __name__ == '__main__':Obj(2)

这个案例是使用了Cython的方案直接去修改了数据帧的内容,而这里所使用的f_locals其实本质上就是locals。

经过一番运行,输出结果如下:

{'self': <__main__.Obj object at 0x7fea2e2
a1e80>, 'y': 2, 'z': 2, 'm': 2, 'n': 3}2

此时局部变量z是被成功修改了的,但是在前面提到的,即使我们通过这种方法修改了局部变量的值,

但是依然不能通过这个方案去创建一个新的局部变量,此时去执行print (n)的话,依然会有报错提示。

读取和修改全局变量

相比于修改局部变量,其实查看修改全局变量要显的更加容易。

首先我们用一个示例演示一下如何查看所有的全局变量:

x = 0class Obj:def __init__(self,y):self.func(y)def func(self, y, z=1):m = 2print (globals())if __name__ == '__main__':Obj(2)

获取局部变量的方式有很多,但是获取全局变量一般就是globals或者等价的f_globals。上述代码执行输出如下:

{'__name__': '__main__', '__doc__': None, '__package__': None,'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f202632ac40>,'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,'__file__': 'xxx.py', '__cached__': None, 'x': 0, 'Obj': <class '__main__.Obj'>}

用这种方法我们发现了全局变量x,而在同一个函数内的几个局部变量,就没有显示在globals的key中。

而不同于locals变量的是,globals函数返回的是一个真实的数据,是可以直接修改,并且在全局生效的。

比如我们在函数内定义或者修改全局变量:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
x = 0class Obj:def __init__(self,y):self.func(y)def func(self, y, z=1):global mm = 2globals()['x']=3if __name__ == '__main__':Obj(2)print(globals()['x'])print(globals()['m'])# 3# 2

在这个例子中我们就可以发现,不仅仅是修改的x值生效了,新建的m也同步到了全局变量中,

这样就可以比较容易的划分全局变量和局部变量再进行统一赋值或者修改。

读取和修改成员变量

在python中每一个定义的object都有一个隐藏属性__dict__,这是一个字典,其中包含了所有的成员变量名和成员变量值。

在前一篇博客中,我们就介绍了通过__dict__去给类中的成员变量进行赋值,非常的方便。

我们可以通过一个示例来看看__dict__中所包含的内容:

x = 0class Obj:def __init__(self,y):self.m = 2self.func(y)def func(self, y, z=1):print (self.__dict__)if __name__ == '__main__':Obj(2)# {'m': 2}

从输出结果中我们就可以看到,__dict__输出的内容非常的纯净,就是所有的成员变量名和变量值。

而成员变量虽然是一个对象的属性,但是其操作方式跟全局变量globals是非常接近的,不像locals一样只读,具体示例如下:

x = 0class Obj:def __init__(self,y):self.m = 2self.func(y)def func(self, y, z=1):self.m = 5self.__dict__['n'] = 6print (self.__dict__)print (self.m, self.n)if __name__ == '__main__':Obj(2)# {'m': 5, 'n': 6}# 5# 6

在这个案例中,我们修改了成员变量的值,也使用__dict__新建了一个成员变量的值,

可以看到最终都有同步到变量空间中,这样就完成了成员变量的修改。

总结

Python本身是一门比较灵活便捷的编程语言,但是便捷往往有可能伴随着一些风险,

比如exec和eval等内置函数的实现,有可能导致sandbox escaping的问题。

而有时候我们又需要一些批量化的操作,比如批量化的创建或者修改局部、全局或者是成员变量,
这样就需要我们首先要把所有的变量名存成字符串,在需要的时候再作为变量名去调用。

在这篇文章中,我们介绍了一系列非exec和eval的操作(并不是说没有风险,也引用了ctype和sys定义的数据帧),来查看和定义、修改所需的各种变量。

尾语

最后感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

最后,宣传一下呀~👇👇👇更多源码、资料、素材、解答、交流皆点击下方名片获取呀👇👇

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

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

相关文章

Ubuntu2204+ROS2(humble)+usb_cam内参标定

1、安装usb_cam包 pip install pydantic1.10.14sudo apt install ros-humble-usb-cam# 测试打开相机 ros2 launch usb_cam camera.launch.py# 显示图像 ros2 run image_view image_view image:/camera1/image_raw 2、安装 camera_calibration sudo apt install ros-humble-c…

爬虫学习笔记-get请求获取豆瓣电影排名多页数据★★★★★

1. 导入爬虫需要使用的包 import urllib.request import urllib.parse 2.创建请求函数 def create_request(page): # 定义不变的url部分 base_url https://movie.douban.com/j/chart/top_list?type5&interval_id100%3A90&action& # 根据规律定义data拼接url …

HCIA真机实验:三层交换机实现vlan之间的通信(内含配置命令)

基础实验示例&#xff1a; 最上面那个交换机作为三层交换机。 下面的两个交换机的配置与之前单臂路由实现vlan之间的通信的配置相同。在这个基础上开启三层交换机 在三层交换机上的配置&#xff1a; 1、创建vlan&#xff08;底下的交换机有多少个vlan&#xff0c;则三层交换…

算法沉淀——前缀和(leetcode真题剖析)

算法沉淀——前缀和 01.一维前缀和02.二维前缀和03.寻找数组的中心下标04.除自身以外数组的乘积05.和为 K 的子数组06.和可被 K 整除的子数组07.连续数组08.矩阵区域和 前缀和算法是一种用于高效计算数组或序列中某个范围内元素之和的技巧。它通过预先计算数组的前缀和&#xf…

MyBatis详解(4) -- 动态SQL

MyBatis详解&#xff08;4&#xff09; 动态SQL两种方式&#xff1a;1.映射器配置文件&#xff1b; 2.注解常用的动态 SQL 元素if元素where元素set元素choose元素&#xff08;优先&#xff0c;只执行一个&#xff09;trim元素foreach 元素&#xff08;批量处理&#xff09;bind…

【云原生】docker-compose单机容器集群编排工具

目录 一、docker-compose容器编排的简介 二、docker-compose的使用 1、docker-compose的安装 2、docker-compose的配置模板文件yaml文件的编写 &#xff08;1&#xff09;布尔值类型 &#xff08;2&#xff09;字符串类型 &#xff08;3&#xff09;一个key有多个值 &am…

Linux——shell程序的简单实现

shell程序的简单实现 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的.xmind和.png文件都已同步导入至资源&#xff0c;可免费查阅 在学习完有关进程的知识后&#xff0c;我们就可以开始尝试自己实现一个简单的shell程序了。 注&#xff1a;在编写简单的shell程…

Spring - 基本用法参考

Spring 官方文档 Spring容器启动流程&#xff08;源码解读&#xff09; BeanFactoryPostProcessor vs BeanPostProcessor vs BeanDefinitionRegistryPostProcessor&#xff1a; From java doc&#xff1a; BeanFactoryPostProcessor may interact with and modify bean defin…

openGaussdb5.0单点企业版部署_Centos7_x86

本文档环境&#xff1a;CentOS7.9 x86_64 4G1C40G python2.7.5 交互式初始化环境方式 介绍 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合华为在数据库领域多年的经验&#xff0c;结合企业级场景需求&#xff0c;持续…

群晖安装Drive Server与Office实现团队固定公网地址远程办公

文章目录 本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 本教程解决的问题是&#xff1a; 1.Word&#xff0c;PPT&#xff0c;Excel等重要文件存在本地环境&#xff0c;如何在编…

基于Prompt Learning的信息抽取

PTR: Prompt Tuning with Rules for Text Classification 清华&#xff1b;liuzhiyuan&#xff1b;通过规则制定subpromptRelation Extraction as Open-book Examination: Retrieval-enhanced Prompt Tuning Relation Extraction as Open-book Examination: Retrieval-enhance…

防御保护(2-6)

第二天 防火墙 防火墙的主要职责---控制和防护---安全策略---翻过去可以根据安全策略来抓取流量之后做出对应的动作。 防火墙分类 按物理特性划分&#xff1a;软件防火墙、硬件防火墙 按性能划分&#xff1a;百兆级防火墙、千兆级防火墙 按防火墙结构划分&#xff1a;单一主机…