Python中的作用域规则详解

Python是静态作用域语言,尽管它自身是一个动态语言。也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,但是Python与C在作用域方面的差异还是非常明显的。

接下来会谈论Python的作用域规则,在这中间也会说明一下Python与C在作用域方面的不同。

在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域;在Python 2.2中,Python正式引入了一种新的作用域 --- 嵌套作用域;在Python 2.1中,嵌套作用域可以作为一个选项被开启;嵌套作用域的引入,本质上为Python实现了对闭包的支持,关于闭包的知识,网上有很多解释,这里就不详细展开了。相应地,变量查找顺序由之前的LGB变成LEGB(L:Local,E:Enclosing,G:Global,B:Built-in)。

在Python中,并不是任何代码块都能引入新的作用域,这与C有很大的不同:

代码如下:

#include<stdio.h>
int main() {
    if(2 > 0) {
        int i = 0;
    }
    printf("i = %d", i);
    return 0;
}

在这段代码中,if子句引入了一个局部作用域,变量i就存在于这个局部作用域中,但对外不可见,因此,接下来在printf函数中对变量i的引用会引发编译错误。

但是,在Python中却并非如此:

代码如下:

if True:
    i = 0
print i

在这段代码中,if子句并没有引入一个局部作用域,变量i仍然处在全局作用域中,因此,变量i对于接下来的print语句是可见的。

实际上,在Python中,只有模块,类以及函数才会引入新的作用域,其它的代码块是不会引入新的作用域的。

在Python中,使用一个变量之前不必预先声明它,但是在真正使用它之前,它必须已经绑定到某个对象;而名字绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量,不论这个名字绑定发生在当前作用域中的哪个位置。

代码如下:

def f():
    print i
f()

运行结果将显示:NameError: global name 'i' is not defined。Python首先在函数f的本地作用域中查找变量i,查找失败,接着在全局作用域和内置作用域中查找变量i,仍然失败,最终抛出NameError异常。

代码如下:

i = 0
def f():
    i = 8
    print i
f()
print i

运行结果显示:8和0。i = 8是一个名字绑定操作,它在函数f的局部作用域中引入了新的变量i,屏蔽了全局变量i,因此f内部的print语句看到的是局部变量i,f外部的print语句看到的是全局变量i。

代码如下:

i = 0
def f():
    print i
    i = 0
f()

运行结果显示:UnboundLocalError: local variable 'i' referenced before assignment。在这个例子当中,函数f中的变量i是局部变量,但是在print语句使用它的时候,它还未被绑定到任何对象之上,所以抛出异常。

代码如下:

print i
i = 0

不论是以交互的方式运行,还是以脚本文件的方式运行,结果都显示:NameError: name 'i' is not defined。这里的输出结果又与上一个例子不同,这是因为它在顶级作用域(模块作用域)的缘故。对于模块代码而言,代码在执行之前,没有经过什么预处理,但是对于函数体而言,代码在运行之前已经经过了一个预处理,因此不论名字绑定发生在作用域的那个位置,它都能感知出来。Python虽然是一个静态作用域语言,但是名字查找确实动态发生的,因此直到运行的时候,才会发现名字方面的问题。

在Python中,名字绑定在所属作用域中引入新的变量,同时绑定到一个对象。名字绑定发生在以下几种情况之下:

1.参数声明:参数声明在函数的局部作用域中引入新的变量;
2.赋值操作:对一个变量进行初次赋值会在当前作用域中引入新的变量,后续赋值操作则会重新绑定该变量;
3.类和函数定义:类和函数定义将类名和函数名作为变量引入当前作用域,类体和函数体将形成另外一个作用域;
4.import语句:import语句在当前作用域中引入新的变量,一般是在全局作用域;
5.for语句:for语句在当前作用域中引入新的变量(循环变量);
6.except语句:except语句在当前作用域中引入新的变量(异常对象)。

在Python中,类定义所引入的作用域对于成员函数是不可见的,这与C++或者Java是很不同的,因此在Python中,成员函数想要引用类体定义的变量,必须通过self或者类名来引用它。

嵌套作用域的加入,会导致一些代码编译不过或者得到不同的运行结果,在这里Python解释器会帮助你识别这些可能引起问题的地方,给出警告。

locals函数返回所有的局部变量,但是不会返回嵌套作用域中的变量,实际上没有函数会返回嵌套作用域中的变量。

(0)

相关推荐

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

    理解全局变量和局部变量 1.定义的函数内部的变量名如果是第一次出现, 且在=符号前,那么就可以认为是被定义为局部变量.在这种情况下,不论全局变量中是否用到该变量名,函数中使用的都是局部变量.例如: num = 100 def func(): num = 123 print num func() 输出结果是123.说明函数中定义的变量名num是一个局部变量,覆盖全局变量.再例如: num = 100 def func(): num += 100 print num func() 输出结果是:Unb

  • 解读Python编程中的命名空间与作用域

    变量是拥有匹配对象的名字(标识符).命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典. 一个Python表达式可以访问局部命名空间和全局命名空间里的变量.如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量. 每个函数都有自己的命名空间.类的方法的作用域规则和通常函数的一样. Python会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的. 因此,如果要给全局变量在一个函数里赋值,必须使用global语句. global VarName的

  • 讲解python参数和作用域的使用

    本文会介绍如何将语句组织成函数,还会详细介绍参数和作用域的概念,以及递归的概念及其在程序中的用途.一. 创建函数函数是可以调用,它执行某种行为并且返回一个值.用def语句即可定义一个函数:(并非所有的函数都会返回一些东西) 复制代码 代码如下: def fibs(num):    result = [0,1]    for i in range(num-2):        result.append(result[-2]+result[-1])    return result 记录函数如果想

  • 讲解Python中for循环下的索引变量的作用域

    我们从一个测试开始.下面这个函数的功能是什么? def foo(lst): a = 0 for i in lst: a += i b = 1 for t in lst: b *= i return a, b 如果你觉得它的功能是"计算lst中所有元素的和与积",不要沮丧.通常很难发现这里的错误.如果在大堆真实的代码中发现了这个错误就非常厉害了.--当你不知道这是一个测试时,很难发现这个错误. 这里的错误是在第二个循环体中使用了i而不是t.等下,这到底是怎么工作的?i在第一个循环外应该是

  • Python 如何访问外围作用域中的变量

    在表达式中引用变量时,Python 会按照如下的顺序遍历各个作用域,寻找该变量: 当前函数作用域 任何外围作用域(比如包含当前函数的其他函数) global 作用域,即代码所在的模块的作用域 如果上述作用域内都找不到变量,就会报 NameError 异常. 但是对变量赋值时,规则会有所不同. 如果当前作用域变量已存在,那么其值会被替换. 如果不存在,则会视为在当前作用域定义新变量,而不是向外围作用域中寻找. 如下函数 def function(): flag = True def helper(

  • Python中的作用域规则详解

    Python是静态作用域语言,尽管它自身是一个动态语言.也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,但是Python与C在作用域方面的差异还是非常明显的. 接下来会谈论Python的作用域规则,在这中间也会说明一下Python与C在作用域方面的不同. 在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域:在Python 2.2中,Python正式引入了一种新的作用域 --- 嵌套作用域:在Python 2

  • Python函数中的作用域规则详解

    目录 1.简单介绍一下闭包 2.在Python中,并不是任何代码块都能引入新的作用域 3.在Python中,名字绑定在所属作用域中引入新的变量,同时绑定到一个对象. 总结 Python是静态作用域语言,但是它自身是一个动态语言.在Python中变量的作用域是由变量在代码中的位置决定的,与C语言有些相似,但不是完全一样. 在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域:在Python2.2中,Python正式引入了一种新的作用域 — 嵌套作

  • python flask中动态URL规则详解

    URL是可以添加变量部分的, 把类似的部分抽象出来, 比如: @app.route('/example/1/') @app.route('/example/2/') @app.route('/example/3/') def example(id): return 'example:{ }'.format(id) 可以抽象为: @app.route('/example/<id>/') def wxample(id): return 'example:{ }'.format(id) 尖括号中的内

  • 浅谈Python中的作用域规则和闭包

    在对Python中的闭包进行简单分析之前,我们先了解一下Python中的作用域规则.关于Python中作用域的详细知识,有很多的博文都进行了介绍.这里我们先从一个简单的例子入手. Python中的作用域 假设在交互式命令行中定义如下的函数: >>> a = 1 >>> def foo(): b = 2 c = 3 print "locals: %s" % locals() return "result: %d" % (a + b

  • python中异常的传播详解

    目录 1.异常的传播 2.如何处理异常 1.异常的传播 当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播.如果函数中没有对异常进行处理,则异常会继续向函数调用者传播.如果函数调用者处理了异常,则不再传播,如果还没有处理,则继续向他的调用者传播,直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并且显示异常信息到控制台.所以异常的传播我们也称之为抛出异常. 异常传播示例如下: def fn1(): print('Hello fn') print(10/0) def

  • Python中的flask框架详解

    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务.本文参考自Flask官方文档,大部分代码引用自官方文档. 安装flask 首先我们来安装Flask.最简单的办法就是使用pip. pip install flask 然后打开一个Python文件,输入下面的内容并运行该文件.然后访问localhost:5000,我们应当可以看到浏览器上输出了hello world. from flask import Flask app = Flask(

  • python中lambda匿名函数详解

    在Python中,不通过def来声明函数名字,而是通过lambda关键字来定义的函数称为匿名函数 关键字lambda表示匿名函数 语法 lambda 参数:表达式 先写lambda关键字,然后依次写匿名函数的参数,多个参数中间用逗号连接,然后是一个冒号,冒号后面写返回的表达式 lambda函数比普通函数更简洁 匿名函数有个好处:函数没有名字,不必担心函数名冲突 匿名函数与普通函数的对比 : def sum_func(a, b, c): return a + b + c sum_lambda =

  • python中模块导入模式详解

    目录 模块导入 1.1 import导入模块 1.2 from 模块名 import 导入模板的方法 1.3 as 关键字 OS模块操作文件 OS模块的作用 模块的制作.发布.安装 3.1 模块制作 3.2 模块的分 3.3 示例 3.4 测试方法 3.5 all魔术方法 模块导入 1.1 import导入模块 所谓的模块其实就是一个外部的工具包,其中存在的其实就是Python文件,这些文件都实现了某种特定的功能,我们导入包之后直接使用即可,非常的方便. 在开发中使用最多的就是使用: impor

  • python中的sys模块详解

    目录 前言 处理命令行参数 sys.stdin.readline()与input sys.stdout与print 总结 前言 sys模块是与python解释器交互的一个接口.sys 模块提供了许多函数和变量来处理 Python 运行时环境的不同部分. 处理命令行参数 在解释器启动后, argv 列表包含了传递给脚本的所有参数, 列表的第一个元素为脚本自身的名称. sys.argv[0] 表示程序自身sys.argv[1] 表示程序的第一个参数sys.argv[2] 表示程序的第二个参数 可以做

  • python中 logging的使用详解

    日志是用来记录程序在运行过程中发生的状况,在程序开发过程中添加日志模块能够帮助我们了解程序运行过程中发生了哪些事件,这些事件也有轻重之分. 根据事件的轻重可分为以下几个级别: DEBUG: 详细信息,通常仅在诊断问题时才受到关注.整数level=10 INFO: 确认程序按预期工作.整数level=20 WARNING:出现了异常,但是不影响正常工作.整数level=30 ERROR:由于某些原因,程序 不能执行某些功能.整数level=40 CRITICAL:严重的错误,导致程序不能运行.整数

随机推荐