python命名空间、作用域、闭包与传值传引用
(以下内容,均基于python3)
最近在看python函数部分,讲到了python的作用域问题,然后又讲了Python的闭包问题。
在做作业的时候,我遇到了几个问题,下面先来看作业。
一、
作业1:
代码A:
def outside():
var = 5
def inside():
var = 3
print(var) inside()
outside()
代码B:
def outside():
var = 5
def inside():
print(var)
var = 3 inside()
outside()
代码A结果:3
代码B的结果:UnboundLocalError: local variable 'var' referenced before assignment
本地变量“var”在被赋值之前,就被引用了
作业2:
def outside():
var = 5 def inside(var):
print(var)
var += 1 inside(var)
print(var) outside()
结果:
5
5
作业3:
def outside():
var = [1, 2, 3] def inside(new_var):
print(new_var)
new_var[0] = 8 inside(var)
print(var) outside()
结果:
[1, 2, 3]
[8, 2, 3]
作业4:
1 def outside():
2 var = [1, 2, 3]
3
4 def inside():
5 print(var)
6 var[0] = 8
7
8 inside()
9 print(var)
10
11 out()
结果:
[1, 2, 3]
[8, 2, 3]
作业5:
def outside():
var = [1, 2, 3]
def inside(): print(var)
var = [4, 5, 6] inside()
print(var)
outside()
结果:UnboundLocalError: local variable 'var' referenced before assignment
本地变量“var”在被赋值之前,就被引用了
我当时,对这4个作业比较迷。。。然后,我就研究了一下,发现问题本质是:1.python变量的作用域问题 2.python的函数传递参数是传值or传引用
要想搞清楚“闭包”,就要搞清楚“作用域”,要想搞清楚“作用域”,就要搞清楚“命名空间”
关系如下:
闭包——>作用域——>命名空间
二、python变量的作用域问题
1.命名空间:
1.1什么是命名空间?
Namespace命名空间,也称名字空间,是从名字到对象的映射。
命名空间的一大作用是避免名字冲突
1 def fun1():
2 i = 1
3
4 def fun2():
5 i = 2
同一个模块中的两个函数中,两个同名名字i之间绝没有任何关系,因为它们分属于不同明明空间。
如果还不清楚,就打个比方:
A名字叫作“张三”;然后,B名字也叫作“张三”。当AB俩人分别在各自的家里的时候,如果有人呼唤“张三”这个名字,A和B都知道叫的是自己,而A和B两个人除了名字相同,都叫做“张三”以外,两个人没有任何关系。如果A和B后来,上学了,在同一个班级,那么当老师叫“张三”这个名字的时候,AB就分不清到底叫的是不是自己了。
这里面,AB这两个人就是变量;“张三”就是变量名;A的家族、B的家族、学校,就是三个不同的命名空间。
名字就是一个指代和引用,目的是:通过提到“名字”,我们能方便快速地找到“名字”主人的本体,当名字发生冲突时,指代发生混乱,命名空间可以帮我们避免这种冲突。
1.2命名空间的分类:
命名空间分3类:内置命名空间、全局命名空间、局部命名空间。
(1)内置命名空间:built-in名字集合,包括像abs()这样的函数,以及内置的异常名字等。通常,使用内置这个词表示这个命名空间-内置命名空间。
(2)全局命名空间:模块全局名字集合,直接定义在模块中的名字,如类,函数,导入的其他模块等。通常,使用全局命名空间表示。
(3)局部命名空间:函数调用过程中的名字集合,函数中的参数,函数体定义的名字等,在函数调用时被“激活”,构成了一个命名空间。通常,使用局部命名空间表示。
注意:1.一个对象的属性集合,也构成了一个命名空间。但通常使用objname.attrname的间接方式访问属性,而不是直接访问,故不将其列入命名空间讨论。
2.类定义的命名空间,通常解释器进入类定义时,即执行到class ClassName:语句,会新建一个命名空间。(见官方对类定义的说明)
1.3命名空间的生命周期:
(1)内置命名空间,在Python解释器启动时创建,解释器退出时销毁;
(2)全局命名空间,模块的全局命名空间在模块定义被解释器读入时创建,解释器退出时销毁;
(3)局部命名空间,这里要区分函数以及类定义。函数的局部命名空间,在函数调用时创建,函数返回或者由未捕获的异常时销毁;类定义的命名空间,在解释器读到类定义创建,类定义结束后销毁。(关于类定义的命名空间,在类定义结束后销毁,但其实类对象就是这个命名空间内容的包装,见官方对类定义的说明)
2.作用域:
2.1什么是作用域?
作用域,这个词听起来很高大上,我觉得,就是一块代码(区域)

(1)变量分为两种引用方式:
- 直接引用:直接使用名字访问的方式,如
name,这种方式尝试在名字空间中搜索名字name。 - 间接引用:使用形如
objname.attrname的方式,即属性引用,这种方式不会在命名空间中搜索名字attrname,而是搜索名字objname,再访问其属性。
- 直接引用:直接使用名字访问的方式,如
2.2作用域与命名空间是什么关系?
名字作用域就是名字可以影响到的代码文本区域,命名空间的作用域就是这个命名空间可以影响到的代码文本区域。那么也存在这样一个代码文本区域,多个命名空间可以影响到它。
运行时的作用域,是按照特定层次组合起来的命名空间。
2.3名字的搜索规则:
如果,python程序中,引用了一个名字,python怎样搜索这个名字呢?
- Local :首先搜索,包含局部名字的最内层(innermost)作用域,如函数/方法/类的内部局部作用域;
- Enclosing:根据嵌套层次从内到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封闭函数的作用域。如两个嵌套的函数,内层函数的作用域是局部作用域,外层函数作用域就是内层函数的 Enclosing作用域;
- Global:倒数第二次被搜索,包含当前模块全局名字的作用域;
- Built-in:最后被搜索,包含内建名字的最外层作用域。
Python按照以上L-E-G-B的顺序依次在四个作用域搜索名字。没有搜索到时,Python抛出NameError异常。
总结:作用域优先级:L-E-G-B
2.4现在再回头来,上面的作业:
作业1:代码A
1 def outside():
2 var = 5
3 def inside():
4 var = 3
5 print(var)
6
7 inside()
8 outside()
结果是:3

代码运行顺序:8-1-2-7-3-4-5
8:当Python解释器运行第8行代码时,调用outside()函数;
1:运行第1行,创建了outside的局部作用域Local(outside);
2:运行第2行;
7:运行第7行,调用inside()函数,跳转到第3行;
3:运行第三行,创建inside的局部作用域Local(inside)。
此时,在inside()函数内部:由于函数嵌套,outside()是inside()的外层函数,inside()是最里层函数,所以,Local(inside)是局部作用域,Local(outside)是Local(inside)的Enclosing作用域。
所以,名字的搜索顺序为:Local(inside)——Local(outside)——Global——Built-in
4:运行第4行,在Local(inside)内部创建一个变量var。
5:运行第5行,print(var),根据Local(inside)——Local(outside)——Global——Built-in的顺序,搜索名字为var的变量
所以,输出3,而不是5。
7:inside()函数执行完毕,返回第7行,Local(inside)作用域销毁。
8:outside()函数执行完毕,返回第8行,Local(outside)作用域销毁。
代码B:
1 def outside():
2 var = 5
3 def inside():
4 print(var)
5 var = 3
6
7 inside()
8 outside()
结果:UnboundLocalError: local variable 'var' referenced before assignment
本地变量“var”在被赋值之前,就被引用了

代码执行顺序:8-1-2-7-3-4-5
8-1-2-7-3:python执行,同代码A
3:运行第3行,创建Local(inside)局部作用域
4:python解释器运行第4行,print(var),从Local根据Local(inside)——Local(outside)——Global——Built-in的顺序,搜索名字为var的变量。
发现,在最内部局部作用域Local(inside),无法找到名字为“var”的变量!
然后,python解释器,会继续执行完inside()这个函数!!!
在第5行,发现赋值语句:var = 3
则,python解释器就认为,var这个变量是属于Local(inside)局部作用域的。
既然对变量 b 的赋值(声明)发生在 print 语句之后, print 语句执行时,变量 b 是还未被声明的,于是抛出错误:变量在赋值前就被引用。
代码2:
1 def outside():
2 var = 5
3
4 def inside(var):
5 print(var)
6 var += 1
7
8 inside(var)
9 print(var)
10
11 outside()
结果:
5
5
原因:
第4行,内部函数声明了形参var,在Local(inside)局部作用域内部,创建了一个新的名为“var”的变量
第8行,通过调用函数inside(var),传参,将Local(outside)作用域的变量var的值5,传递给内部的Local(outside)作用域的变量var,使它的初始值也为5。
第5行,打印变量var,得5
第6行,var += 1,内部函数(作用域Local(inside))的变量var的值,变为6。
返回第8行,inside()函数执行完毕,内部作用域Local(inside)随之销毁。
第9行,名字搜索顺序为:Local(outside)——Global——Built-in,所以输出打印outside()作用域中的变量var的值,5.
python命名空间、作用域、闭包与传值传引用的更多相关文章
- python基础(7)-函数&命名空间&作用域&闭包
函数 动态参数 *args def sum(*args): ''' 任何参数都会被args以元组的方式接收 ''' print(type(args)) # result:<class 'tupl ...
- [Python] 命名空间&作用域
Python的类语句不会创建实例 类会创建命名空间,通过对象访问类的属性和方法 类不会创建作用域,对方法和属性的引用必须加以限定(如在方法中必须通过self引用实例的属性) class My1(): ...
- python中的类变量和对象变量,以及传值传引用的探究
http://www.cnblogs.com/gtarcoder/p/5005897.html http://www.cnblogs.com/mexh/p/9967811.html
- python变量作用域,函数与传参
一.元组传值: 一般情况下函数传递参数是1对1,这里x,y是2个参数,按道理要传2个参数,如果直接传递元祖,其实是传递一个参数 >>> def show( x, y ): ... p ...
- 成对使用new和delete,传值传引用
首先: delete []p;是用来删除对象数组的,特别是你声明的是对象数组!!! 如果new中用了[],delete一定要用[]:在new中没有使用,在delete中一定不要使用. 其次: 当你使用 ...
- 简谈Java传值传引用
本随笔旨在强化理解传值与传引用 如下代码的运行结果 其中i没有改变,s也没有改变. 但model中的值均改变了. i :100s :hellomodel :testchangemodel2 :ch ...
- python 嵌套作用域 闭包函数
#闭包函数 def multiplier(factor): def multiplyByFactory(number): return number*factor return multiplyByF ...
- python命名空间与闭包函数详解
python命名空间与闭包函数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客主要介绍的知识点如下: 1>.三元运算 2>.命名空间 3>.globa ...
- python—命名空间、作用域查找顺序、闭包
名称空间 name space,如下图: x = 1, 1存放在内存中,1 会有一个内存地址,x 则 存放在 name space 里,并同时记录了 1的内存地址, 即 名称空间是存放了变量x与1绑定 ...
随机推荐
- BZOJ 3227: [Sdoi2008]红黑树(tree)
BZOJ 3227: [Sdoi2008]红黑树(tree) 标签(空格分隔): OI-BZOJ OI-其它 Time Limit: 10 Sec Memory Limit: 128 MB Descr ...
- 2017.10.14 Java的流程控制语句switch&&随机点名器
今日内容介绍 1.流程控制语句switch 2.数组 3.随机点名器案例 ###01switch语句解构 * A:switch语句解构 * a:switch只能针对某个表达式的值作 ...
- 2017.10.10 java中的继承与多态(重载与重写的区别)
1. 类的继承 继承是面向对象编程技术的主要特征之一,也是实现软件复用的重要手段,使用继承特性子类(subclass) 可以继承父类(superclass)中private方法和属性,继承的目的是使程 ...
- 关于win10深度学习安装配置 CUDA9.0+VS2017+Cudnn7.4.1.5+Anaconda3(cupy安装包)+python3.7+pycharm
0 查看电脑系统版本(非常重要) WIN+R->输入winver, 系统版本号必须高于1703,否则CUDA9.0难以运行!!!! 1 安装 NVIDIA 显卡驱动程序 下载地址:驱动程序 选择 ...
- 【洛谷P1379】八数码难题(广搜、A*)
八数码难题 题目描述 一.广搜: 首先要考虑用什么存每一个状态 显然每个状态都用一个矩阵存是很麻烦的. 我们可以考虑将一个3*3的矩阵用一个字符串或long long 存. 每次扩展时再转化为矩阵. ...
- Spring的jdbcTemplate 与原始jdbc 整合c3p0的DBUtils 及Hibernate 对比 Spring配置文件生成约束的菜单方法
以User为操作对象 package com.swift.jdbc; public class User { private Long user_id; private String user_cod ...
- C#把日期转化成星期
显示效果: ***** 前台页面代码: <TextBlock Grid.Row="/> <TextBlock Grid.Row="/> ...
- 2018/7/16 YMOI模拟 NOIP2013D2T3华容道
题目描述 Description 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间. ...
- 8-1 python 接口开发(提供数据、返回session_id)
1.接口开发,根据不同查询条件返回数据库查询结果 import flask import tools import json server = flask.Flask(__name__) #新建一个服 ...
- node服务端渲染(完整demo)
简介 nodejs搭建多页面服务端渲染 技术点 koa 搭建服务 koa-router 创建页面路由 nunjucks 模板引擎组合html webpack打包多页面 node端异步请求 服务端日志打 ...