从局部变量和全局变量开始全面解析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删除服务器文件代码示例
Feb 09 Python
python 3利用Dlib 19.7实现摄像头人脸检测特征点标定
Feb 26 Python
使用python根据端口号关闭进程的方法
Nov 06 Python
对python调用RPC接口的实例详解
Jan 03 Python
对python多线程SSH登录并发脚本详解
Feb 14 Python
Python将列表数据写入文件(txt, csv,excel)
Apr 03 Python
决策树剪枝算法的python实现方法详解
Sep 18 Python
详解基于python-django框架的支付宝支付案例
Sep 23 Python
简单了解python装饰器原理及使用方法
Dec 18 Python
PyTorch 解决Dataset和Dataloader遇到的问题
Jan 08 Python
django中嵌套的try-except实例
May 21 Python
记一次Django响应超慢的解决过程
Sep 17 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
为什么《星际争霸》是测试人工智能的理想战场
2019/12/03 星际争霸
用libTemplate实现静态网页的生成
2006/10/09 PHP
PHP程序开发范例学习之表单 获取文本框的值
2011/08/08 PHP
Linux系统下php获得系统分区信息的方法
2015/03/30 PHP
Zend Framework教程之Zend_Controller_Plugin插件用法详解
2016/03/07 PHP
PHP解压ZIP文件到指定文件夹的方法
2016/11/17 PHP
解决php extension 加载顺序问题
2019/08/16 PHP
JS中prototype关键字的功能介绍及使用示例
2013/07/21 Javascript
js中符号转意问题示例探讨
2013/08/19 Javascript
javascript中创建对象的几种方法总结
2013/11/01 Javascript
jquery的attr方法禁用表单元素禁用输入内容
2014/06/23 Javascript
JS判断变量是否为空判断是否null
2014/07/25 Javascript
js实现input框文字动态变换显示效果
2015/08/19 Javascript
简单谈谈javascript Date类型
2015/09/06 Javascript
详解javascript实现自定义事件
2016/01/19 Javascript
JQuery学习总结【一】
2016/12/01 Javascript
jQuery插件FusionWidgets实现的Cylinder图效果示例【附demo源码】
2017/03/23 jQuery
chorme 浏览器记住密码后input黄色背景处理方法(两种)
2017/11/22 Javascript
react实现点击选中的li高亮的示例代码
2018/05/24 Javascript
Vuex mutitons和actions初使用详解
2019/03/04 Javascript
深入理解Node内建模块和对象
2019/03/12 Javascript
jQuery实现简单评论区功能
2020/10/26 jQuery
python中pycurl库的用法实例
2014/09/30 Python
Python中非常实用的一些功能和函数分享
2015/02/14 Python
两个命令把 Vim 打造成 Python IDE的方法
2016/03/20 Python
python基础教程之分支、循环简单用法
2016/06/16 Python
Win8下python3.5.1安装教程
2020/07/29 Python
自定义html标记替换html5新增元素
2008/10/17 HTML / CSS
快速实现一个简单的canvas迷宫游戏的示例
2018/07/04 HTML / CSS
SmartBuyGlasses德国:购买太阳镜和眼镜
2019/08/20 全球购物
Java TransactionAPI (JTA) 主要包含几部分
2012/12/07 面试题
params有什么用
2016/03/01 面试题
大学四年职业生涯规划书范文
2014/01/02 职场文书
三严三实对照检查材料思想汇报
2014/09/28 职场文书
幼儿园亲子活动感想
2015/08/07 职场文书
大学生心理健康教育心得体会
2016/01/12 职场文书