变量的作用域

起因

最近闲来无事, 在 Python 官网上看到了2.0版本, 是2001年的.

image-20210116142522829

打算装起来体验一下最初发布的版本, 但是发现只有 Windows 版本, 所以我就装了个 Windows10的虚拟机, 就在我打算安装的时候, 发现:

image-20210116142802711

这激起了我的好胜欲, 于是我就依次安装了Windows 8, Windows 7, Windows XP, 功夫不负有心人, 终于在XP系统上装上了. (现在的很多网站, 在XP系统的 ie 上已经打不开了. )

想必大家没怎么见过Python 2.0的安装过程 吧, 在此截图留念:








中间各种试用, 在此按下不表, 来看一下出问题的代码:

x = 2def re_f():x = 3def tmp_f():print xtmp_f()re_f()

这段代码的输出结果是什么? 按常理来说, 应该是3, 没错吧. 但是, 你看:

image-20210116144427156

??? 什么鬼? 为什么读到了全局变量? 我还特地有到Python 3.0的环境中跑了一遍, 发现结果确实是3啊. 不懂就要问, 于是我开始搜寻各种资料, 发现这设计到了变量的作用域.

回顾历史

要想理解这个现象, 就得把时间线往回拉, 拉到什么时候呢? 就从汇编说起.

在早期的汇编中, 对一个变量定义后, 就作为全局变量作用于整个程序. 在编译之后, 将所有该变量名替换为正确的地址, 相当于维护了一个变量名到地址的映射表.

当然, 这并没有什么问题, 但是随着时间推移, 程序的规模越来越大, 问题就出现了.

你定义了一个变量 x=2, 调用了一个系统函数之后, 回来发现x变成9了. 因为系统函数中也存在变量x, 这很明显会引发各种各样的问题, 开发难度大幅度提升.

如何解决这个问题呢? 出现问题的根源就是, 定义的变量都是全局变量, 每个修改其变量的人, 都会影响所有使用者. 接下来有了各种解决办法:

长变量名

既然出问题的原因是使用了同名变量, 那我让所有变量的名字都不一样就可以嘛.

在函数sort中的所有变量, 都加上_sort后缀, 比如变量i, 就定义为i_sort, 但无法避免另外一个sort函数, 那就在后缀再拼上一个文件名? 但如果文件名也一样呢? 毕竟很多时候, 你需要调用各种现有的库, 你无法保证没有冲突.

很显然, 这并不能解决本质问题.

变量回写

既然同名这个方向走不通了, 那就往全局方向使劲吧. 如果能让变量只在当前函数起作用, 而不会被其他人随意修改, 不就能够解决这个问题了么?

说起来容易, 如何实现呢? 如果说, 我在函数退出的时候, 把变量再改回我进来时候的样子, 不就能假装什么都没有发生吗? 比如这样:

function test(){// 这里用到了变量 i, 那就先把原来的值记下来$old_i = $i;// 然后就可以随意对变量 i 修改了// 返回时将变量改回去$i = $old_i;return;
}

但是, 这种处理方法有如下问题 :

问题1: 若old_i变量也是个全局变量怎么办

对于这个问题还是很好处理的, 编译器是有全局变量的对照表的, 随便找一个不存在的变量还是很容易的, 这个赋值的操作直接交给编译器来处理就好.

问题2: 上层函数的修改会影响下层函数

举个简单的例子:

$i = 1;function fun_1(){$old_i = $i;$i = 2;fun_2();$i = $old_i;return;
}

这里有一个全局变量i, 在fun_2中读到的变量i值是多少? 是2. 函数fun_1本无意修改i的值, 但其修改还是影响了所有下层函数. 当然, 有时确实需要读取上层函数的修改, 但是, 也有很多情况是要读到其原始值的.

动态作用域

无法读取到全局变量的原因, 是变量的值在上层函数中已经被修改了, 其原本的值已经不存在了. 如何实现真正的局部变量, 保证不会对全局变量造成污染呢? 很简单, 只要函数的变量与全局变量, 实际指向的地址不同就可以了. 如何实现呢?

函数使用一张自己的变量名对照表, 就可以了. 大概就长这样:

image-20210116160120405

这样, 函数使用的变量就是真正的局部变量了. 当函数fun_1退出的时候, 会将对应的对照表销毁.

这个时候, 函数fun_2读取变量$i的时候, 会按照对照表的创建顺序, 在fun_2变量对照表, fun_1变量对照表, 全局变量对照表 依次查找, 看哪一个先找到.

哎, 这不就是闭包么. 动态作用域读取变量的结果, 其实与上方的回写变量的方式差不多, 不同的是, 动态作用域保留了全局变量原始的值. 既然原始值留下来了, 那自然就要能够读到, 否则留他何用, 读取的方式就是下面的静态作用域了.

静态作用域

静态作用域也是通过变量的对照表来实现, 与动态作用域不同的是, 每个函数能看到的变量对照表只有自己的和全局的, 上面的函数调用, 换成静态作用域大概如下:

image-20210116160536236

这样就能让函数绕过上层, 直接访问全局变量了.

现象

了解了变量作用域相关内容, 也就能够解释最开始遇到的现象了.

再来回看一下最开始的问题, 为什么在Python 2.0中, 闭包读取到的变量是全局变量呢? 很明显, 其使用了静态作用域导致的. 那么在2.0中如何解决这个问题呢? 传参, 修改之后的代码:

x = 2def re_f():x = 3def tmp_f(x):print xtmp_f(x)re_f()

再次执行, 结果与预期一致, 是3

而到了Python 2.1.3就已经改为动态作用域了. (也不知道为什么2.1比2.2还要晚一年发布)

在函数中如果想修改外部变量, 需要对变量进行声明, 若不声明则创建本地变量. 在 Python 中有两个关键字对变量进行声明:

  • global: 声明全局变量, 既通过静态作用域的方式查找变量
  • nolocal: 通过动态作用域的方式查找变量

当然, Python中通过上面关键字标识的变量修改, 会直接修改外层变量的值, 个人还是推荐以返回值的形式处理.

我是真的闲, 为了装Python2.0我就搞了半天, 查作用域又查了三四个小时.

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

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

相关文章

PHP8的注解

PHP8.0增加了注解的支持, 虽然 PHP的注解没用过, 但是咱用过JAVA的注解呀. 注解这玩意怎么用? 简单说就下面几步: 定义注解类使用注解提取注解 到了PHP中, 也基本上换汤不换药. 使用 定义注解类 #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION)] cl…

HBase 数据存储结构

在HBase中, 从逻辑上来讲数据大概就长这样: 单从图中的逻辑模型来看, HBase 和 MySQL 的区别就是: 将不同的列归属与同一个列族下支持多版本数据 这看着感觉也没有那么太大的区别呀, 它解决了 MySQL 的那些问题呢? 每一个新事物的出现, 都是为了解决原本存在的问题. 对写入…

spark计算操作整理

spark 的计算流程大概如图: 其中, 通过多次处理, 生成多个中间数据, 最后对结果进行操作获得数据. 本文不涉及任何原理, 仅总结spark在处理的时候支持的所有操作, 方便后面使用的时候, 可以参照本文进行数据的处理. 以下函数整理, 基与Python中RDD对象. 数据的转换操作 数据…

软件工程模型

你在工作中, 软件的开发流程是怎样的? 你是否想过, 除了你当前使用的流程, 还存在其他怎样的流程? 现在的流程有哪些问题, 又能够如何解决? 别说, 前辈们已经给出了一些项目流程的模型, 既软件工程. 可以简单了解一下, 带动一下我这生了锈的脑子. 在很久以前, 一个软件的从…

路径.git下的文件

用了这么久的git, 可以毫不谦虚地说对git是一无所知. 每天用来用去的就是commit, add, merge 等几个有限的命令, 这不符合我这刨根问底的性格啊. 不行, 得研究研究, 从哪里下手呢? 别的咱先不说, 所有 git 项目都有这么一个文件夹.git, 不如就从它入手 ? 那咱就看看这个文件夹…

git 操作二进制文件

平常用git进行项目管理已经稀松平常了, 今天咱来点不一样的. 平常管理的都是普通的文本文件, 如果是二进制文件, git能够处理么? 比如word文档. 测试一下. 新建一个项目, 在其中创建test1.docx, test2.txt两个空文件并提交. 之后编辑文件并添加标题, git diff看一下效果: 效…

搭建个人博客

一直都想着搞一个自己的个人博客, 拖着这么久, 最近终于开始动手了. 故留下一篇完整的记录, 若你也刚好有相同的需要, 那这篇文章应该恰好能够帮助到你. 准备 云服务器个人域名 如果没有云服务器, 将应用跑在自己的电脑上, 通过内网穿透大概也能达到效果, 但是个人电脑实在没…

nginx 配置文件的匹配规则

引出 之前在对php-fpm 进行nginx代理时, 为了对后台限定 IP 访问, 添加了如下配置: location ^~ /admin {allow 127.0.0.1;deny all; }结果呢? 所有admin路径下的php文件, 全都没有解析, 变成文件下载了. 当时我不知道是什么问题, 不过将这段配置去掉之后, 问题就消失了. 所…

计算机是如何进行时间同步的

WHY 在网络世界中, 各个计算机之间要想协同工作, 时间同步是一个十分重要的基础. 在计算机内部是有自己的时间的, 这个时间通过内部的晶体振荡器差生的固定频率, 来模拟时间流逝进行计算. 虽然频率十分稳定, 但也是有误差的, 虽然现在的工艺水平误差已经十分小了. (关于震荡的…

WordPress架构简单剖析

前言 最近在搭建自己的博客站点时, 选择了网站使用较多的WordPress, 随着慢慢的使用, 它灵活的插件和主题令我折服. 基本上任何想要实现的功能, 都可以在上面通过插件的形式进行添加. 无论是在访问前的缓存、访问后的统计、访问中的过滤、各种流程的修改等等, 几乎都能够以插件…

阿里云定时任务并自动释放

前言 最近写了一个爬虫脚本, 脚本跑在一台北京的 ecs 上. 但奈何因某种未知力量, 需要连接代理才能访问目标网站. 本来想着自己搭代理, 但是太贵了, 就暂时搁置了. 直到我发现了这个: 阿里云香港的服务器, 一个小时才5分钱. 如果脚本直接跑在香港服务器上不就可以了咩, 按照这…

智能优化算法应用:基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.金豺算法4.实验参数设定5.算法结果6.参考文献7.MA…

PHP-PDO参数绑定问题

前言 今天在执行这样一段代码: $data [username > hujingnb,address > beijing, ]; $dbh new PDO("mysql:host{$host};dbname{$dbname}", $username, $password); $statement $dbh->prepare(INSERT INTO test_user (username, address) VALUES (:usern…

Python 的协程

前言 最近在看部分Python源码时, 发现了async 这个关键字. 查了一下发现了Python中的协程. 协程这玩意, 在GO中我用过啊, 简单说, 就是一个轻量级的线程嘛, 由语言自己来实现不同协程的调度. 想着Python中可能也是差不多的东西吧. 但是我Google搜了一下, 前面的说明都给出了下…

虚拟内存分页机制的地址映射

概述 在之前的文章虚拟内存对分页机制做了简单的介绍. 还有一个疑问, 那就是如何将虚存中的逻辑地址映射为物理地址呢? 今天就来简单分析一下. 对于一个分页的地址来说, 一般包含两个元素: 页号: 第几页偏移量: 当前页的第几个字节 以下以 addr_virtual(p, o)表示一个逻辑…

虚拟内存分页机制的页面置换

前言 之前简单介绍过虚拟内存是如何与物理内存进行地址映射的: 虚拟内存分页机制的地址映射, 但是仅仅地址映射是不够的, 在地址映射说过会有缺页的情况, 此时就需要操作操作系统将缺少的页加载到内存中. 但是, 如果内存满了怎么办呢? 毕竟虚拟内存一般都要大于物理内存的, 不…

Kubernetes各个组件的概念

前言 Kubernetes中的概念太多了, 什么Pod Service Deployment 等等等等, 给刚接触的我都整蒙了. 通过几天观察下来, 说一下我对各个组件的理解. 此文章仅仅对这些概念做一个简单的介绍, 不至于后面看其他文章的时候一头雾水. Node Node很好理解. 就是服务实际运行的实例, 可…

Kubernetes中Pod生命周期

在 Kubernetes中Pod是容器管理的最小单位, 有着各种各样的Pod管理器. 那么一个Pod从启动到释放, 在这期间经历了哪些过程呢? Pod自开始创建, 到正常运行, 再到释放, 其时间跨度及经历的阶段大致如下: 说一下各个阶段的作用以及是为了解决什么问题. 容器调度和下载镜像的过程就…

wait函数的作用

前言 在编写C程序的时候, 通过fork函数来创建新的进程, wait函数来等待子进程结束. 那么就有一个问题了, 什么情况下父进程需要等待子进程结束后继续执行呢? 如果需要等待子进程结束, 那直接将操作放到父进程执行不就醒了么? 反正等着也是等着. 当然, 还有有一种情况, 任务…

OAuth1.0介绍

背景 为什么需要OAuth授权呢? 最典型的应用场景就是第三方登录了, 我们开发了一个网站希望用户可以QQ登录, 但是怎么能拿到用户的 QQ 信息呢? 用户将 账号密码告诉我们当然可以, 但是这样有如下隐患: 我们拿到了用户的密码, 这样很不安全. 而且任意一个应用被黑, 所有相关…