初学者教程之命名空间,范围解析及LEDB规则

2014年5月12日

Sebastian Raschka编写

这是一篇关于采用LEGB规则实现Python变量命名空间及范围解析的简短教程。下面章节将会提供简短的可以说明问题的示例代码块来简要阐述问题。您可以简单的从头至尾阅读本教程,但我鼓励您去执行这些代码段。你可以复制粘贴这些代码段,但是为了方便您也可以下载IPython笔记

章节

? 章节

? 目标

? 命名空间和范围介绍

o 命名空间

o 范围

o 提示:

o 通过LEGB规则解析变量名的范围

?1. LG-本地和全局范围

o 原因:

o 原因:

?2. LEG – 局部、封闭和全局范围

o 原因:

?3. LEGB – 局部、封闭、全局、内置

o 原因:

? 自由练习

? 结论

o 经验法则

o 解决方案

o 警告:对于循环变量“leaking”加入全局命名空间

目标

?命名空间和范围:Python是从哪里寻找变量名?

?我们可以在同一时间定义或重用多个对象的变量名吗?

?Python是通过哪种方式为变量名搜索不同的命名空间的呢?

命名空间和范围介绍

命名空间

大致来说,命名空间只是将名称映射到对象的容器。正如你可能已经听过,Python中所有的字符串、列表、函数、类等等都是对象。如此,“对象与名称”的映射关系允许我们使用已经为这个对象分配的名称来访问这个对象。例如,如果我们做一个简单的字符串分配,可以通过:a_string = “Hello string”, 我们创建了一个关于“Hello string”的对象,从此以后我们就可以通过它的变量名a_string来访问它。

我们可以想象一个命名空间为Python字典结构,字典的键在哪里代表它的名字以及字典的值在哪里代表对象本身(这也是目前在Python中命名空间该如何实现),例如,

a_namespace = {‘name_a‘:object_1, ‘name_b‘:object_2, ...}

现在,棘手有部分是在Python中我们有多个独立的命名空间,并且名称可以被不同的命名空间重复使用(只有对象是唯一的,例如,

a_namespace = {‘name_a‘:object_1, ‘name_b‘:object_2, ...

b_namespace = {‘name_a‘:object_3, ‘name_b‘:object_4, ...}

例如,当我们调用一个循环或定义一个函数时,它将创建自己的命名空间。命名空间也有不同的层次(所谓的“范围”),我们将在下一节中更详细地讨论。

范围

在上述章节中,我们已经了解到命名空间可以独立存在,彼此和他们的结构在一定的层次,这给我们带来了“范围”。Python 中的“范围”在我们为“命名对象”映射搜索命名空间时定义了“层级”。

例如,让我们思考一下下面的这段代码:

在这里,我们仅仅只对变量名i定义了两次,一次是是在foo函数中。

?foo_namespace = {‘i‘:object_3, ...}

?global_namespace = {‘i‘:object_1, ‘name_b‘:object_2, ...}

所以,如果我们想要打印变量i的值,Python是如何知道应该查找哪个命名空间呢?这个地方就需要Python的LEGB规则来实现了,我们将在下个章节讨论。

提示:

如果我们想要打印出字典的全局变量和局部变量的映射,我们可以使用global()和local()函数。

通过LEGB规则解析变量名范围

我们已经看到,多个命名空间可以彼此独立存在,它们可以在不同的层次水平包含相同的变量名。“范围”定义在哪个层次,Python为其相关对象搜索了一个特定的“变量名”。现在,下一个问题是:Python是采用什么方式在它找到对象名称的映射之前搜索命名空间的不同级别呢?

答案是:Python使用LEGB规则,即

     局部->封闭->全局->内置

箭头应该指向命名空间层次结构搜索顺序的方向。

      ?局部可以在函数内部或类方法中,例如。

       ?封闭可以是它的enclosing方法,例如,一个方法被包含在另一个方法中。

       ?全局是指执行脚本的最高级别,以及

       ?内置是Python自己保留的特殊名称。

所以,如果一个特定的名称:对象映射不能在本地命名空间中找到,下一步封闭范围的命名空间将会被搜索。如果在封闭范围的搜索也是不成功的,Python将会到全局命名空间去搜索,最终,它将搜索内置命名空间(附注:如果名称在任何的命名空间都找不到,系统将报告NameError)。

注:

命名空间还可以嵌套,例如如果我们导入的模块,或者如果我们定义新类。在这种情况下,我们必须使用前缀来访问这些嵌套的命名空间。让我在下面的代码块中说明这个概念:

(这也是为什么我们在通过“from a module import *”时必须注意,因为在加载变量名到全局命名空间时很可能会覆盖已经存在的变量名。)

1. LG – 局部和全局范围

例 1.1

作为热身练习,让我们先忘记在LEGB规则中封闭(E)和内置(B)的范围,来看一看LG----局部和全局范围。

下面的代码将打印什么呢?

原因:

我们首先调用a_func(),这应该是打印a_var值。根据LEGB规则,这个方法将会首先在局部范围(L)查看是否a_var是被定义的。因为a_func()没有定义自己a_var,它会向上一级全局范围(G)查找,直到a_var之前定义的范围。

例 1.2

现在,让我们在全局和局部范围定义变量a_var.

你能猜到下面的代码将会产生什么?

原因:

当我们调用a_func()时,首先它会在局部范围(L)中查找a_var,因为a_var已经在局部范围a_func中定义,它的赋值local varible就会被打印。注意这不会影响在不同范围的全局变量。

然后,它也在可能会修改全局的,例如,如果我们在重新赋值时使用全局关键字。下面的例子将会说明:

但是我们必须小心这个顺序:如果我们没有明确的告诉Python我们想要使用全局范围来尝试修改变量值就很容易出现UnboundLocalError错误。(记住,赋值操作先执行的是右边的操作):

2. LEG – 局部, 封闭和全局范围

现在,让我们来介绍一下封闭范围(E)的概念。按照“局部->封闭->全局”的顺序,你能猜出下面的代码将打印什么吗?

例 2.1

原因:

让我们快速概括我们在做什么: 我们调用outer(), 其变量a_var在局部被定义(a_var在全局存在)。接下来, outer()函数调用同样定义了名字为a_var的变量的inner()函数。Inner()中的print()函数在它的范围层级结构之前先在局部范围中查找(L->E),因此它打印了在局部范围内分配的值。

类似于我们已经在前面章节中看到的global关键字的概念,我们可以使用包含在内置函数的关键字nonlocal来明确地访问外部(封闭)范围的变量,以修改它的变量值。

注意nonlocal关键字已经添加到了Python 3.x, 在Python 2.x中还没有实现。


3. LEGB – 局部,封闭,全局,内置

总结LEGB规则,让我们进入内置范围。在这里,我们定义了我们“own”的长度函数,这恰好为内建的len()函数具有相同的名称。如果我们执行了下面的代码,结果是什么?

例 3

原因:

因为相同的名称可用于映射不同的对象,只要这个名字在不同的名字空间并且没有重用的名字来定义自己的函数len长度的问题(这只是用于演示,不推荐)。当我们回顾Python的L - >E> G > B层次,函数a_func()发现在它尝试搜索内置(B)命名空间之前len()已经在全局范围(G)。

自由练习

现在,在我们经过几次联系之后,让我们快速检查我们掌握到哪里?因此,多一次的练习:下面的代码将打印出什么呢?


结论

我希望通过这个简短的教程可以有助于理解Python利用LEGB规则来进行范围解析顺序的基本的概念。我想鼓励你(作一次小的自由练习)明天再去看看代码段,再检查一下你是否能正确的预测出它们的结果。

经验法则

在实践中,在函数范围修改全局变量通常是一个坏想法,因为它往往会引起一些混乱和奇怪的错误,并且很难调试。

如果你想通过函数修改一个全局变量,建议你通过参数传入该全局变量并将返回值重新指定给全局变量。

例如:


解决方案

为上防止你无意的破坏,我已经用二进制格式编写了解决方案。 为了显示这些字符表示,你只需要执行下面的几行代码:


警告:对于循环变量“leaking”加入全局命名空间

和其他的变成语言相反,for-loops将使用他们存在的范围并留下他们已经定义的循环变量。

这也使用于如果我们明确在全局命名空间之前定义for-loop变量!在这种情况下,它会重新绑定存在的变量。

无论如何,在Python 3.x中,我们可以使用闭包来防止循环变量将其分割到全局命名空间中。

为什么我提到“Python 3.x”?  因为当问题发生时,相同的代码在Python 2.x的执行结果是这样的:

英文原文:http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html
译者:linkxu1989

时间: 2024-11-05 21:50:39

初学者教程之命名空间,范围解析及LEDB规则的相关文章

Xamarin XAML语言教程XAML文件结构与解析XAML

Xamarin XAML语言教程XAML文件结构与解析XAML XAML文件结构 在上文中,我们创建XAML文件后,会看到类似图1.16所示的结构 图1.16  结构 其中,.xaml文件和.xaml.cs文件就是XAML文件的结构.以下就是对这两个文件的介绍. .xaml文件中包含的就是XAML代码,实际上就是XML语法.官方的说法:它是一个声明对象的语言,为我们创建对象提供便捷的一种方式.与HTML类似,特点是用来描述用户接口 (UI)内容. 通常我们把与.xaml文件关联的.xaml.cs

命名空间--名称解析规则

(PHP 5 >= 5.3.0, PHP 7) PHP 命名空间中,类名可以通过三种方式引用: 非限定名称(Unqualified name),名称中不包含命名空间分隔符的标识符,如Foo. 例子 $a=new foo(); 或 foo::staticmethod();.如果当前命名空间是currentnamespace,foo 将被解析为currentnamespace\foo.如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo. 限定名称(Quali

Apache Solr初学者教程(入门之旅)

Apache Solr初学者教程(入门之旅) 写在前面:本文涉及solr入门的各方面,请逐行阅读,相信能帮助你对solr有个清晰全面的了解和使用. 在Apache Solr初学者教程的这个例子中,我们将讨论有关如何安装最新版本的Apache Solr,并告诉你如何配置它.此外,我们将告诉你如何进行使用solr的样本数据文件索引.Apache Solr支持不同格式,包括各种数据库,PDF文件,XML文件,CSV文件等等.在这个例子中,我们的索引将研究如何索引数据从一个CSV文件. 我们首选的这个例

Quick-Cocos2d-x初学者游戏教程(三) ---------------------------- 解析quick新建项目的代码文件

Quick-Cocos2d-x初学者游戏教程(三) 2.main.lua 在src目录下,除了 config.lua 文件外,还有一个 main.lua 文件,这个 main.lua 是 Quick 项目的通用入口文件,它类似于 Cocos2d-x 中的 AppDelegate.h/cpp 文件,同时也类似于一般 Windows 工程中的 main 文件. 打开 main.lua 文件,其内容如下所示: 1 2 3 4 5 6 7 8 9 10 function __G__TRACKBACK__

JS命名空间模式解析

简介 在SF上看到这样一个提问: 如题,因为不得已的原因,需要写若干个全局函数.但又不想这样: window.a = function(){} window.b = function(){} window.c = function(){} 题主问有什么好的写法? 解答: 如果你用 jQuery,你可以这样写 $.extend(window, { a: function() {}, b: function() {}, c: function() {} }); 如果你不用 jQuery,可以直接实现

Github初学者教程(一)

如果你是一名程序员,或者是相关专业的学生,那么Github你不应不知道.很多开源组织和大神,会选择在Github这个平台上,发布他们的开源项目,学会使用Github将能够给你的学习和工作带来巨大帮助!可是Github的使用依靠的是Git命令或者是客户端,对于初学者来说入门有一点麻烦,网上相关的教程很多都是copy别人的,学起来会遇到很多不明白的地方,于是有了这一篇博客. 一.注册Github账号  github官网地址:https://github.com/,有了账号之后就可以建立创建仓库了(C

IbatisNet初学者教程

一.什么是 IbatisNet   : 应该有很多人都听说过Ibatis吧,没错IbatisNet就是Ibatis在.net上的一个移植版本,所以它们俩基本是差不多的.所以IbatisNet自然也是一个轻量级的ORM框架,于是我们又想起来了hibnate和Nhibnate,学习了IbatisNet  ,我会与 Nhibnate做一个简单的对比. 严格的来说IbatisNet并不是一种很正统的ORM解决方案.因为它不像NHibernate那样,具备全自动的数据操作,包括查询,插入,更新,删除,也没

SpringMVC源代码学习外传(一)xml命名空间的解析NamespaceHandler

以下内容基于书:<看透SpringMVC-源代码分析与实践> 基本照搬...用于自己查阅备忘. 加上外传关键字的是讨论一些SpringMVC的特定类的使用方法, 非外传的是对启动流程/请求处理流程,我们可以看作主线剧情. 我们知道springMVC的配置都是写在xml文件里的,这些配置是怎么从xml读取的呢? 解析它们的是一个接口 位置在spring-beans-RELEASE.jar内org.springframework.beans.factory.xml.NamespaceHandler

当xml中存在命名空间,dom4j解析以及写入xml文档时的乱码问题

最近公司项目开发中需要通过前台用户界面进行客户业务系统的部署(提供界面化操作,减少运维工作的难度),通过修改web.xml进行设置各个项目不同的信息配置. 开发过程中遇到2种问题,同时将解决方案备注上,以方便日后查看. 问题一:当xml中存在命名空间,三种处理办法(dom4j) 问题二:文件保存之后总是提示中文乱码问题 针对上面2个问题的解决方案进行汇总,解决方法主要还是来自于其他网络同行的博客. 第一个 问题主要参照 博客http://blog.sina.com.cn/s/blog_5cef6