从局部变量和全局变量开始全面解析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开发常用的一些开源Package分享
Feb 14 Python
Python Requests安装与简单运用
Apr 07 Python
Python自动化运维和部署项目工具Fabric使用实例
Sep 18 Python
详解python3百度指数抓取实例
Dec 12 Python
SQLite3中文编码 Python的实现
Jan 11 Python
Python实现通过文件路径获取文件hash值的方法
Apr 29 Python
学习python中matplotlib绘图设置坐标轴刻度、文本
Feb 07 Python
Python中的pack和unpack的使用
Mar 12 Python
Python DataFrame 设置输出不显示index(索引)值的方法
Jun 07 Python
python装饰器简介---这一篇也许就够了(推荐)
Apr 01 Python
django Admin文档生成器使用详解
Jul 22 Python
Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法
May 26 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 Mysql类 可以参考学习熟悉下
2009/06/21 PHP
PHP文件上传问题汇总(文件大小检测、大文件上传处理)
2015/12/24 PHP
php实现购物车功能(以大苹果购物网为例)
2017/03/09 PHP
Laravel中的Blade模板引擎示例详解
2017/10/10 PHP
PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】
2019/06/14 PHP
JSChart轻量级图形报表工具(内置函数中文参考)
2010/10/11 Javascript
JavaScript使用slice函数获取数组部分元素的方法
2015/04/06 Javascript
JS实现鼠标框选效果完整实例
2016/06/20 Javascript
输入法的回车与消息发送快捷键回车的冲突解决方法
2016/08/09 Javascript
javascript实现的左右无缝滚动效果
2016/09/19 Javascript
jQuery实现删除li节点的方法
2016/12/06 Javascript
nodejs中向HTTP响应传送进程的输出
2017/03/19 NodeJs
利用nginx + node在阿里云部署https的步骤详解
2017/12/19 Javascript
sortable+element 实现表格行拖拽的方法示例
2019/06/07 Javascript
vue实现登录页面的验证码以及验证过程解析(面向新手)
2019/08/02 Javascript
微信小程序点击生成朋友圈分享图(遇到的坑)
2020/06/17 Javascript
Jquery Fade用法详解
2020/11/06 jQuery
[40:19]2018完美盛典CS.GO表演赛
2018/12/17 DOTA
Django URL传递参数的方法总结
2016/08/28 Python
Django中Model的使用方法教程
2018/03/07 Python
python 字典中文key处理,读取,比较方法
2018/07/06 Python
Django项目使用ckeditor详解(不使用admin)
2019/12/17 Python
如何打包Python Web项目实现免安装一键启动的方法
2020/05/21 Python
Python基于BeautifulSoup爬取京东商品信息
2020/06/01 Python
Python实现自动装机功能案例分析
2020/10/22 Python
在html5的Canvas上绘制椭圆的几种方法总结
2013/01/07 HTML / CSS
澳大利亚冒险体验:Adrenaline(跳伞、V8赛车、热气球等)
2017/09/18 全球购物
企业管理部经理岗位职责
2013/12/24 职场文书
财务管理专业自荐信范文
2013/12/24 职场文书
关于打架的检讨书
2014/01/17 职场文书
小学教师师德承诺书
2014/05/23 职场文书
2014最新党员批评与自我批评材料
2014/09/24 职场文书
师德承诺书2015
2015/04/28 职场文书
迎新生欢迎词2015
2015/07/16 职场文书
Maven学习----Maven安装与环境变量配置教程
2021/06/29 Java/Android
Nginx限流和黑名单配置
2022/05/20 Servers