从局部变量和全局变量开始全面解析Python中变量的作用域


Posted in Python onJune 16, 2016

理解全局变量和局部变量
1.定义的函数内部的变量名如果是第一次出现, 且在=符号前,那么就可以认为是被定义为局部变量。在这种情况下,不论全局变量中是否用到该变量名,函数中使用的都是局部变量。例如:

num = 100
  def func():
    num = 123
    print num
  func()

输出结果是123。说明函数中定义的变量名num是一个局部变量,覆盖全局变量。再例如:

num = 100
  def func():
    num += 100
    print num
  func()

输出结果是:UnboundLocalError: local variable 'num' referenced before assignment。提示错误:局部变量num在赋值前被应用。也就是说该变量没有定义就被错误使用。由此再次证明这里定义的是一个局部变量,而不是全局变量。

2.函数内部的变量名如果是第一次出现,且出现在=符号后面,且在之前已被定义为全局变量,则这里将引用全局变量。例如:

num = 100
  def func():
    x = num + 100
    print x
  func()

输出结果是200。如果变量名num在之前没有被定义为全局变量,则会出现错误提示:变量没有定义。例如:

def func():
    x = num + 100
    print x
  func()

输出结果是:NameError: global name 'num' is not defined。

3.函数中使用某个变量时,如果该变量名既有全局变量也有局部变量,则默认使用局部变量。例如:

num = 100
  def func():
    num = 200
    x = num + 100
    prinx x
  func()

输出结果是300。

4.在函数中将某个变量定义为全局变量时需要使用关键字global。例如:

num = 100
  def func():
    global num
    num = 200
    print num
  func()
  print num

输出结果分别是200和200。这说明函数中的变量名num被定义为全局变量,并被赋值为200。再例如:

num = 100
  def func():
    global num
    num = 200
    num += 100
    print num
  func()
  print num

输出结果分别是300和300。

结合上文对全局变量和局部变量的应用场景的整理结果,我尝试对input fields中的教学代码的前半部分做一些分析(中文部分的注释):

# calculator with all buttons

  import simplegui

  # intialize globals
  store = 0
  operand = 0

这里调用了simplegui模块,可以在http://www.codeskulptor.org/操作无误。但是该模块无法直接在python环境中使用,需要先安装SimpleGUICS2Pygame包。

# event handlers for calculator with a store and operand

  def output():
  """prints contents of store and operand"""
    print "Store = ", store
    print "Operand = ", operand
    print ""

在定义的函数output()中直接使用了全局变量store和operand。可以参考第2点。

def swap():
  """ swap contents of store and operand"""
    global store, operand
    store, operand = operand, store
    output()

在定义的函数swap()中首先对store和operand做了全局变量的定义。如果不这样操作,那么就会出现没有赋值就被使用的错误提示。可以参考第1点。同时是不是可以这样理解:函数swap()中,在没有关键字global的情况下,store和operand是默认局部变量,而=右边的部分在没有赋值的情况被使用是错误的。可以参考第3点。

def add():
  """ add operand to store"""

    global store
    store = store + operand
    output()

在这里我碰到了两周课程学习以来的第一个难题:那就是为什么add()函数中只定义了store为全局变量,而没有相同地去定义operand。现在结合第1点来看,是因为store作为局部变量没有事先赋值,不能直接使用,而operand是可以直接调用之前定义的全局变量来使用的。

变量作用域
变量作用域(scope)在Python中是一个容易掉坑的地方。
Python的作用域一共有4中,分别是:

L (Local) 局部作用域
E (Enclosing) 闭包函数外的函数中
G (Global) 全局作用域
B (Built-in) 内建作用域
以 L --> E --> G -->B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

Python除了def/class/lambda 外,其他如: if/elif/else/  try/except  for/while并不能改变其作用域。定义在他们之内的变量,外部还是可以访问。

>>> if True:
...   a = 'I am A'
... 
>>> a
'I am A'

定义在if语言中的变量a,外部还是可以访问的。
但是需要注意如果if被 def/class/lambda 包裹,在内部赋值,就变成了此 函数/类/lambda 的局部作用域。
在 def/class/lambda内进行赋值,就变成了其局部的作用域,局部作用域会覆盖全局作用域,但不会影响全局作用域。

g = 1 #全局的
def fun():
  g = 2 #局部的
  return g

print fun()
# 结果为2
print g
# 结果为1

但是要注意,有时候想在函数内部引用全局的变量,疏忽了就会出现错误,比如:

#file1.py
var = 1
def fun():
  print var
  var = 200
print fun()

#file2.py
var = 1
def fun():
  var = var + 1
  return var
print fun()

这两个函数都会报错UnboundLocalError: local variable 'var' referenced before assignment
在未被赋值之前引用的错误!为什么?因为在函数的内部,解释器探测到var被重新赋值了,所以var成为了局部变量,但是在没有被赋值之前就想使用var,便会出现这个错误。解决的方法是在函数内部添加 globals var 但运行函数后全局的var也会被修改。

闭包Closure
闭包的定义:如果在一个内部函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

函数嵌套/闭包中的作用域:

a = 1
def external():
  global a
  a = 200
  print a

  b = 100
  def internal():
    # nonlocal b
    print b
    b = 200
    return b

  internal()
  print b

print external()

一样会报错- 引用在赋值之前,Python3有个关键字nonlocal可以解决这个问题,但在Python2中还是不要尝试修改闭包中的变量。 关于闭包中还有一个坑:

from functools import wraps

def wrapper(log):
  def external(F):
    @wraps(F)
    def internal(**kw):
      if False:
        log = 'modified'
      print log
    return internal
  return external

@wrapper('first')
def abc():
  pass

print abc()

也会出现 引用在赋值之前 的错误,原因是解释器探测到了 if False 中的重新赋值,所以不会去闭包的外部函数(Enclosing)中找变量,但 if Flase 不成立没有执行,所以便会出现此错误。除非你还需要else: log='var' 或者 if True 但这样添加逻辑语句就没了意义,所以尽量不要修改闭包中的变量。

好像用常规的方法无法让闭包实现计数器的功能,因为在内部进行 count +=1 便会出现 引用在赋值之前 的错误,解决办法:(或Py3环境下的 nonlocal 关键字)

def counter(start):
    count =[start]
    def internal():
      count[0] += 1
      return count[0]
    return internal

count = counter(0)
for n in range(10):
  print count()
# 1,2,3,4,5,6,7,8,9,10

count = counter(0)
print count()
# 1

由于 list 具有可变性,而字符串是不可变类型。

locals() 和 globals()
globals()
global 和 globals() 是不同的,global 是关键字用来声明一个局部变量为全局变量。globals() 和 locals() 提供了基于字典的访问全局和局部变量的方式

比如:如果函数1内需要定义一个局部变量,名字另一个函数2相同,但又要在函数1内引用这个函数2。

def var():
  pass

def f2():
  var = 'Just a String'
  f1 = globals()['var']
  print var
  return type(f1)

print f2()
# Just a String
# <type 'function'>

locals()
如果你使用过Python的Web框架,那么你一定经历过需要把一个视图函数内很多的局部变量传递给模板引擎,然后作用在HTML上。虽然你可以有一些更聪明的做法,还你是仍想一次传递很多变量。先不用了解这些语法是怎么来的,用做什么,只需要大致了解locals()是什么。
可以看到,locals()把局部变量都给打包一起扔去了。

@app.route('/')
def view():
  user = User.query.all()
  article = Article.query.all()
  ip = request.environ.get('HTTP_X_REAL_IP',     request.remote_addr)
  s = 'Just a String'
  return render_template('index.html', user=user,
      article = article, ip=ip, s=s)
  #或者 return render_template('index.html', **locals())
Python 相关文章推荐
python基于Tkinter库实现简单文本编辑器实例
May 05 Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
Mar 23 Python
Python查找两个有序列表中位数的方法【基于归并算法】
Apr 20 Python
Python迭代器定义与简单用法分析
Apr 30 Python
情人节快乐! python绘制漂亮玫瑰
Aug 18 Python
python集合是否可变总结
Jun 20 Python
pyqt5移动鼠标显示坐标的方法
Jun 21 Python
简单了解python gevent 协程使用及作用
Jul 22 Python
python__name__原理及用法详解
Nov 02 Python
基于python连接oracle导并出数据文件
Apr 28 Python
深入浅析Python代码规范性检测
Jul 31 Python
Python Matplotlib绘制等高线图与渐变色扇形图
Apr 14 Python
实例讲解Python中global语句下全局变量的值的修改
Jun 16 #Python
最大K个数问题的Python版解法总结
Jun 16 #Python
Python中的多行注释文档编写风格汇总
Jun 16 #Python
Python构造自定义方法来美化字典结构输出的示例
Jun 16 #Python
浅谈Python中chr、unichr、ord字符函数之间的对比
Jun 16 #Python
详解Python中 __get__和__getattr__和__getattribute__的区别
Jun 16 #Python
Python利用带权重随机数解决抽奖和游戏爆装备问题
Jun 16 #Python
You might like
PHP Ajax JavaScript Json获取天气信息实现代码
2016/08/17 PHP
实例讲解PHP页面静态化
2018/02/05 PHP
js中的window.open返回object的错误的解决方法
2009/08/15 Javascript
javascript for循环从入门到偏门(效率优化+奇特用法)
2012/08/01 Javascript
javascript实现tabs选项卡切换效果(自写原生js)
2013/03/19 Javascript
导入extjs、jquery 文件时$使用冲突问题解决方法
2014/01/14 Javascript
JavaScript-RegExp对象只能使用一次问题解决方法
2014/06/23 Javascript
javascript实现禁止右键和F12查看源代码
2014/12/26 Javascript
JS+CSS实现淡入式焦点图片幻灯切换效果的方法
2015/02/26 Javascript
利用js+css+html实现固定table的列头不动
2016/12/08 Javascript
vue.js评论发布信息可插入QQ表情功能
2017/08/08 Javascript
jQuery Position方法使用和兼容性
2017/08/23 jQuery
详解React之父子组件传递和其它一些要点
2018/06/25 Javascript
通过js示例讲解时间复杂度与空间复杂度
2019/08/06 Javascript
js数组相减简单示例【删除a数组所有与b数组相同元素】
2020/03/04 Javascript
Python struct.unpack
2008/09/06 Python
在Python程序中操作MySQL的基本方法
2015/07/29 Python
Python实现扩展内置类型的方法分析
2017/10/16 Python
使用python3+xlrd解析Excel的实例
2018/05/04 Python
win10下python3.5.2和tensorflow安装环境搭建教程
2018/09/19 Python
python将list转为matrix的方法
2018/12/12 Python
python write无法写入文件的解决方法
2019/01/23 Python
python 使用turtule绘制递归图形(螺旋、二叉树、谢尔宾斯基三角形)
2019/05/30 Python
用python3 返回鼠标位置的实现方法(带界面)
2019/07/05 Python
django fernet fields字段加密实践详解
2019/08/12 Python
Python实现钉钉订阅消息功能
2020/01/14 Python
python shapely.geometry.polygon任意两个四边形的IOU计算实例
2020/04/12 Python
亚洲航空公司官方网站:AirAsia
2019/11/25 全球购物
教师试用期自我鉴定
2014/02/12 职场文书
财务人员的自我评价范文
2014/03/03 职场文书
诉讼财产保全担保书
2014/05/20 职场文书
2016八一建军节慰问信
2015/11/30 职场文书
anaconda python3.8安装后降级
2021/06/11 Python
Python基础数据类型tuple元组的概念与用法
2021/08/02 Python
Mysql查询时间区间日期列表,不会由于数据表数据影响
2022/04/19 MySQL
java中如何截取字符串最后一位
2022/07/07 Java/Android