Python的名字绑定

在Python中,对象是通过名字进行关联和引用的。Python通过名字绑定操作来引入名字。

Python中的所谓的代码块就是一段作为执行单元的程序。比如:模块、函数、类定义。在交互式环境中输入的命令也是代码块的一种。一个Python脚本文件也是一个代码块。还有就是,当我们在命令行上使用-c选项指定的命令也是一个代码块。传递给内建函数eval()exec()的字符串参数也是代码块的一种。

代码块是以执行帧的方式被执行的,一个执行帧包含了一些管理信息,可以用于调试。执行帧还会在执行完当前的代码块以后指定在何处,以怎样的方式执行接下来的代码。

Python中的作用域定义了名字在代码块中的可见性。如果在代码块中定义了一个局部变量,那么这个局部变量的作用域就是所在的这个代码块。如果这个定义发生在函数体内,则这个变量的作用域就扩展到包含在这个函数中的任何代码块中,但是,如果包含在这个函数中的一个代码块中,同样的名字被绑定到了不同的对象上,那么外面的名字将不能被扩展到这个代码块中。


def out_func():
#a的作用域在out_func这个函数中
a = 0
b = 0
def in_func():
#a的作用域从out_func扩展到了in_func中,因为in_func这个代码块包含在out_func中
print(a) #out_func函数中的b不能扩展到in_func中,因为在in_func中,b重新绑定到了不同的对象上,所以在out_func中的b的作用域不能扩展到in_func中。
b = 1

在Python中,定义在类代码块中名字只能在类中可见,并且类中的名字的作用域不能扩展到类中的方法中。如果在类定义中出现了生成器表达式和列表展开,那么类中的名字也不能扩展到这些表达式中,因为列表展开和生成器表达式的实现都是使用函数作用域的。


class C:
a = 0 # 在列表表达式中,a会因为未定义而抛出NameError异常
b = list(a + i for i in range(10)) def method(self):
#由于定义在类中的名字不能扩展到方法中,所以下面的语句是错误的,会抛出a未定义的NameError异常
print(a)

当在一个代码块中使用一个名字的时候,会对最近的外围作用域进行解析,以查找这个名字。所有的这些在当前代码块中可见的作用域的集合,称为

当前的代码块的环境。

名字绑定和作用域的关系

如果一个名字绑定到一个代码块中,除非这个名字声明为nonlocal(nonlocal声明的作用是:使得变量在外围作用域中,在全局作用域之前被解析),否则这个名字就是这个代码块的局部变量。如果一个名字被绑定到模块级别,则这个名字的作用域是全局的,这个变量是全局变量(模块中的变量,对于模块而言是局部变量,而对于模块中的代码块而言,则是全局变量)。如果一个名字在一个代码块中使用,但是不是在这个代码块中被定义的,则这个变量就是一个自由变量

名字绑定相关的异常

如果在进行名字查找的时候,名字没有被找到,则会抛出一个 NameError 异常,如果名字引用的是一个局部变量,但是这个名字还没有被绑定到这个局部变量,则会抛出一个 UnboundLocalError 异常(UnboundLocalError 是 NameError的子类)。

发生名字绑定行为的情况

发生名字绑定的行为主要有:

  • 通常的给函数传递参数的时候,参数名会和传递过来的对象进行绑定
  • 使用import语句进行导入的时候,其中 from ... import * 语句会将被导入的模块中的所有可以被导入的名字进行绑定操作
  • 类定义的时候
  • 函数定义的时候
  • 进行赋值操作的时候
  • 在for循环的for语句中
  • 在with语句中的as后面
  • 在expect语句中的as后面

Python中的名字绑定的Pitfall

在Python中,名字绑定的一些规则,会导致在使用名字的时候,出现不能理解的错误,特别是对于有C、C++ 和 Java经验的用户。

在Python中,名字绑定操作无论发生在当前块的 任何 位置,在这个代码块中对这个名字的引用都会使用在当前块中绑定的对象。那么,问题就来了,如果我们在名字绑定操作发生之前对这个名字进行了引用,那么就会出现错误,抛出 UnboundLocalError 异常。


>>> a = 10
>>> def function():
print(a)
a = 20 # a的绑定操作发生在print之前 >>> function()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
function()
File "<pyshell#4>", line 2, in function
print(a)
UnboundLocalError: local variable 'a' referenced before assignment

在Python中,代码块中的局部变量可以通过扫描整个代码块来获得绑定的名字,所以在上面的代码中,a这个名字在执行print的时候通过对代码块的扫描已经被找到,但是名字a的绑定操作却还没有发生,所以出现了错误。

在上面的代码中,如果我们需要外面定义的全局变量a,则可以使用global 语句进行声明。


>>> a = 10 >>> def function():
global a
print(a)
a = 20 #这里并不引入新的名字,而是将全局变量a绑定到20上 >>> function()
10 >>> a
20

global 语句的作用是,使得后面对通过这条语句声明的对象的引用,使用的是顶层名字空间中的名字。在顶层名字空间中,包含了全局名字空间和内建名字空间,全局名字空间会首先被搜索,如果没有找到,会对内建名字空间进行搜索。global 语句必须出现在名字使用之前。

如果在外围作用域中的自由变量包含了一个global声明,则这个自由变量被认为是全局的。

内建名字空间

在查找内建名字空间的时候,会访问当前代码块的全局名字空间中的 __builtins__名字,这个名字引用的是一个名字字典或者是一个模块。在 __main__ 模块中, __builtins__ 的引用是内建模块 builtins,然而,如果是在其他模块中, __builtins__ 引用的是 builtins 模块的名字字典。

注意:

CPython的实现中,不能手动修改 __builtins__ 这个变量,如果需要覆盖这个内建名字空间中的名字,需要导入 builtins 模块,然后修改这个模块中相应的属性。


Reference

Naming and binding

Python的名字绑定的更多相关文章

  1. python 封装、绑定

    目录 python 封装.绑定 1.数据.方法的封装 2.隐藏属性 3.开放接口 4.绑定方法 1.对象的绑定 2.类的绑定(classmethod) 3.非绑定方法(staticmethod) 4. ...

  2. Python——面向对象、绑定对象、组合

    1. 面向过程VS面向对象 (1)面向过程 核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. 优点是:极大的降低了写程序的复杂 ...

  3. python 类的绑定方法和非绑定方法

    一.绑定方法 1.对象的绑定方法 首先我们明确一个知识点,凡是类中的方法或函数,默认情况下都是绑定给对象使用的.下面,我们通过实例,来慢慢解析绑定方法的应用. class People: def __ ...

  4. python 给对象绑定属性和方法和__slots__的使用

    # 以c语言为主是静态语言,运行之前先编译,在运行的过程中不允许编辑代码# 在运行的过程中,可以改变,可以添加属性,就是属于动态语言(python) # python动态的添加属性以及方法class ...

  5. 全面解析python类的绑定方法与非绑定方法

    类中的方法有两类: 绑定方法 非绑定方法 一.绑定方法 1.对象的绑定方法 首先我们明确一个知识点,凡是类中的方法或函数,默认情况下都是绑定给对象使用的.下面,我们通过实例,来慢慢解析绑定方法的应用. ...

  6. UnboundLocalError,探讨Python中的绑定

    绑定 将python闭包之前,先梳理一下闭包中的绑定操作. 先看看2个相关的错误 NameError 和UnboundLocalError When a name is not found at al ...

  7. Python基础:绑定和方法调用

    首先,方法仅仅是类内部定义的函数,也就是说,方法是类属性而不是实例属性. 其次方法有两种被调用的方式:调用绑定的方法和调用未绑定的方法. 当存在一个实例时,方法才被认为绑定到了那个实例上,没有实例时方 ...

  8. python学习-名字管理

    记录代码片段 print("="*50) print(" 名片管理系统 v0.01") print("1. 添加一个新的名片") print ...

  9. python概念-各类绑定的概念和property的变态一面

    # 编辑者:闫龙 # 1.什么是绑定到对象的方法,如何定义,如何调用,给谁用?有什么特性 #在类中定义的(self)方法都是绑定到对象的方法 #定义 class a: def b(self):#绑定到 ...

随机推荐

  1. 四核exynos4412开发板使用网线上网注意事项

    问:RP4412开发板板子可以插网线上网? 答:可以.支持WIFI.LAN.3/4G上网的.插网线没? 问:我插了,他还是提示让我连wifi. 答:你是上网页还是其他的APP. 网页可以直接打开,有部 ...

  2. android模块混淆打包时,泛型丢失

    现象:lib模块中写了一个泛型接口,在混淆之后泛型消失,提示"Error:(67, 79) 错误: 类型 ******* 不带有参数" 解决:混淆时把泛型给混淆掉了,在progua ...

  3. php基础_变量和比较符

    本人php菜鸟一枚,初读<php和mysql web开发>,把其中的比较基础的一些东西列举出来,方便和自己一样的菜鸟快速入门,并且方便自己以后复习理解. ①变量 1.比较有特色的应该是变量 ...

  4. html、css基础注意点

    之前第一次接触html,一直使用table进行布局,十分麻烦还相当丑陋,造成当初并没有多大的兴趣,直到半年前开始接触到了使用div+css编写页面,才对它有了兴趣.作为一个菜鸟记录自己的点滴教训与收获 ...

  5. Coding源码学习第三部分(EaseStartView.m)

    首先接上篇的要做一个NSEnumerator 类的延展阅读. 枚举(NSEnumerator) (1)依附于集合类(NSArray,NSSet,NSDictionary),没有用来创建实例的接口. ( ...

  6. Memcached(1)-----基本介绍

    memcached memcached概述 memcached是LiveJournal旗下Danga Interactive公司开发的一款软件.memcached是一个高性能,分布式内存对象缓存系统, ...

  7. Oracle之内存结构(SGA、PGA)

    一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area):由每个服务进程.后台进程专有:每个进程都有一个PGA. ...

  8. HalconMFC(一)之多版本配置

    今天比较匆忙,还得写周六日考试扯P的PPT,就先这样开个头吧.我的电脑是win7,32位的系统,我用Halcon10.0.但是很多小伙伴的都是64位系统的,所以我用小伙伴的64位系统试过很多次用VC配 ...

  9. linux内核学习之七 可执行程序的装载和运行

    一 程序的装载和运行的基本知识补充    1 当进程开始执行一个新的程序时,从父进程继承的所有页被释放,以便在新的用户地址空间开始执行新的计算,甚至进程的特权都可能发生改变,但是,进程的PID不会改变 ...

  10. as3 中文转拼音

    private static const PinYin:Object = {"a":"\u554a\u963f\u9515","ai":&q ...