[Python] Python 之 function, unbound method 和 bound method
首先看一下以下示例。(Python 2.7)
#!/usr/bin/env python
# -*- coding: utf-8 -*- class C(object):
def foo(self):
pass c = C() print 'C.foo id:', id(C.foo), type(C.foo)
print 'c.foo id:', id(c.foo), type(c.foo) a = C.foo
b = c.foo print 'a = C.foo id:', id(a), type(a)
print 'b = c.foo id:', id(b), type(b)
输出:
C.foo id: 2582008 <type 'instancemethod'>
c.foo id: 2582008 <type 'instancemethod'>
a = C.foo id: 2582008 <type 'instancemethod'>
b = c.foo id: 2485624 <type 'instancemethod'>
为什么第1到3行输出id相同,而第4行id则变了呢?
当你通过句号标记法查找一个对象的方法时,例如class.name或者instance.name,Python会自动为该对象创建一个新的method对象。Python使用描述器来将一个function对象封装为method对象。
因此,当你使用id()函数查找C.foo的id时,Python创建了一个新的method对象,当你读取了这个method对象的id(其实就是内存地址)后,这个method对象就被销毁了,因此它所占用的内存被释放出来。接着你用同样的方法查找c.foo的id,一个新的method对象在刚才被释放的内存中被创建并随之销毁,因为前后两个对象都是在同一个内存地址创建,因此你看到的id(内存地址)是一样的。为什么两个method对象都会在读取完id后被销毁呢?这与Python的垃圾回收机制有关,Python使用引用计数器来控制垃圾回收,当一个对象引用次数为0时就会被回收销毁,因此当id()函数执行完毕后,再没有任何其他对象引用这两个method对象,因此它们被销毁释放出内存。
接下来的第13、14行,你将待绑定方法(unbound method)C.foo的一个索引(或者称引用)保存到一个变量a中,因为此时刚刚新建的C.foo对象的索引计数器值为1,不是0,因此它没有被当作垃圾回收并销毁,因此也就不会释放它所占用的内存。随后你使用c.foo创建了第二个method对象,它被指派在另一个新的内存地址上,因此最后你看到的id(a)和id(b)的值是不一样的。
当你使用id()函数查看对象的id时,应该知道以下官方关于id()的描述:
Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value. CPython implementation detail: This is the address of the object in memory. 返回一个对象的唯一标识。该标识为一个整数(或者long类型整数),该整数保证了唯一性,并且伴随所标识的对象的整个生命周期。两个生命周期没有重叠的对象可能具有相同的id值。 CPython的实现细节:充当对象身份标识的整数是对象在内存中的地址。
你也可以使用类的__dict__属性通过索引直接指向目标函数,然后调用目标函数的__get__方法创建一个method对象。
Python 2.7.3 (default, Feb 27 2014, 21:38:55)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class C(object):
... def foo(self):
... pass
...
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x2ec5b0>
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x2ef550>>
以上示例中,第4行创建了一个类C,第8行创建了一个待绑定方法对象(unbound method)C.foo,第10行引用了一个函数对象(function),第12行创建了一个待绑定方法对象C.foo,第14行创建了一个已绑定方法对象(bound method)。
其中第10行所引用的函数对象每次被调用都是引用同一个函数对象,而第8行、12行、14行每次调用都是新创建一个method对象。
function对象的__get__方法负责将函数封装为一个method对象,这个method对象可以是bound method或者unbound method。
>>> def b(x):print "Argument x is ", x
...
>>> b.__get__
<method-wrapper '__get__' of function object at 0x55d770>
>>> b.__get__(None, C)
<unbound method C.b>
>>> b.__get__(C, C)()
Argument x is <class '__main__.C'>
>>> b.__get__(None, C)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method b() must be called with C instance as first argument (got nothing instead)
函数b通过调用__get__方法可以将自己封装为一个method,如果使用一个class对象来调用函数的__get__方法,得到的就是unbound method,使用instance对象来调用函数的__get__方法,得到的就是bound method。函数的__get__方法所实行的封装过程,其实就是将函数绑定到一个对象上,无论是unbound method还是bound method,最终都是绑定到一个类或者实例上,因此这里所谓的unbound和bound其实是针对该方法是否是绑定到实例而言。
对于在类中定义的实例方法,例如上述示例中的class C中的foo方法,c.foo()等同于C.foo(c),不同的是前者的foo是bound method,后者foo是unbound method。
在Python 3K中,已经去除unbound method和bound method两个概念,取而代之的是,C.foo将得到一个函数对象,而c.foo将得到一个方法。新的定义更加合理清晰,不容易使陌生人感到困惑。
之所以做出这样的修正,是因为Python对方法的理解与大多数语言不同,Python 3K对方法的理解是:方法是一个预置了一个实例对象作为第一个参数的函数。预置一个实例对象使得函数变成了一个已绑定方法(bound method)。
而Python 2K对于方法的理解是:一个方法如果没有和一个实例进行绑定,那么它就是一个unbound method,一个unbound method对象可以视为一种函数,这种函数受到一种限制,就是必须将一个实例作为第一个参数传入。于是unbound method就是处于一种待命状态,一旦被绑定就成了一个bound method。
但是,随着时间的脚步,人们发现unbound method就是一个function,而对这个函数作出首个参数必须是正确的实例的限制意义不大,因此在Python 3中就去除了这个限制,使之更加符合鸭子理论。
了解更多《First-class Everything (Python缔造者Guido van Rossum关于bound/unbound method的来历叙述)》
[Python] Python 之 function, unbound method 和 bound method的更多相关文章
- Bound Method and Unbound Method - 绑定方法 与 非绑定方法
Bound Method and Unbound Method 通常有两种方法对类的方法(instance.method)/属性(class.attribute)进行引用, 一种称做 Bound Me ...
- [转载]Python方法绑定——Unbound/Bound method object的一些梳理
本篇主要总结Python中绑定方法对象(Bound method object)和未绑定方法对象(Unboud method object)的区别和联系.主要目的是分清楚这两个极容易混淆的概念,顺便将 ...
- python tips:类的绑定方法(bound)和非绑定方法(unbound)
类属性只有类及其实例能够访问,可以理解为一个独立的命名空间. Python中类属性的引用方式有两种: 1. 通过类的实例进行属性引用,称为绑定方法(bound method),可以理解为方法与实例绑定 ...
- Python OOP(2)-static method,class method and instance method
静态方法(Static Method): 一种简单函数,符合以下要求: 1.嵌套在类中. 2.没有self参数. 特点: 1.类调用.实例调用,静态方法都不会接受自动的self参数. 2.会记录所有实 ...
- python --- Python中的callable 函数
python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...
- Micro Python - Python for microcontrollers
Micro Python - Python for microcontrollers MicroPython
- 从Scratch到Python——python turtle 一种比pygame更加简洁的实现
从Scratch到Python--python turtle 一种比pygame更加简洁的实现 现在很多学校都开设了Scratch课程,学生可以利用Scratch创作丰富的作品,然而Scratch之后 ...
- 从Scratch到Python——Python生成二维码
# Python利用pyqrcode模块生成二维码 import pyqrcode import sys number = pyqrcode.create('从Scratch到Python--Pyth ...
- [Python]Python 使用 for 循环的小例子
[Python]Python 使用 for 循环的小例子: In [7]: for i in range(5): ...: print "xxxx" ...: print &quo ...
随机推荐
- 苹果Mac OS系统修改Hosts文件的方法
使用苹果Mac OS X系统的用户有很多,近期也有不少童鞋问我Mac怎么修改hosts,修改hosts的方式有很多,下面我就整理两种比较方便的方法吧,希望能够帮到大家. 在某些时候可能遇到了需要修改系 ...
- Ogre 编辑器二(用Ogre的地形组件加载天龙八部地形)
主界面如上文设计完成后,场景刚开始添加了是Ogre例子里的,发现场景里实物太少,于是想到直接把天龙的场景拿下来,天龙网上有源码,参考了下,把天龙的地形用Ogre的地形组件完成了下,如下是效果图: 因为 ...
- JUnit规则
在本节中,我们学习和了解JUnit中叫做规则的新功能,它允许非常灵活在测试类重新定义每个测试方法的行为.为了这个目的,@Rule注解被使用来标出测试类的公共字段.这些字段类型为MethodRule,这 ...
- JUnit教程
测试是检查应用程序是否是工作按照要求,并确保在开发者水平,单元测试进入功能性的处理.单元测试是单一实体(类或方法)的测试. 单元测试在每一个软件公司开发高品质的产品给他们的客户是十分必要的. 单元测试 ...
- IntelliJ IDEA 2017 汉化包及教程
一.准备 官网下载IntelliJ IDEA 2017 并安装好 下载汉化包 (百度云链接:http://pan.baidu.com/s/1slS9ZMP 密码:gp79) 二.汉化 此处有两种方法, ...
- Lua--------------------unity3D与Slua融合使用
下载与安装 下载地址 GitHub 安装过程 1.下载最新版,这里, 解压缩,将Assets目录里的所有内容复制到你的工程中,对于最终产品,可以删除slua_src,例子,文档等内容,如果是开发阶段则 ...
- ubuntu -- 系统目录结构
1./:目录属于根目录,是所有目录的绝对路径的起始点,Ubuntu 中的所有文件和目录都在跟目录下. 2./etc:此目录非常重要,绝大多数系统和相关服务的配置文件都保存在这里,这个目录的内容一般只能 ...
- HTML5视频破碎重组特效 强大视觉冲击
HTML5视频播放器很多,但是HTML5视频特效还是很少见的,这款HTML5视频破碎重组特效非常刺激,给人强大的视觉冲击.点击视频任意地方,HTML5将会将这些区域击碎,过一段时间,这些被击碎的视频碎 ...
- linux下nginx安裝
1.yum安裝 yum安裝 http://nginx.org/packages/centos/7/noarch/RPMS/ 第一步: 安裝命令: yum localinstall http://ng ...
- Layer笔记
官网地址:http://layer.layui.com/hello.html 引入代码 <script src="jQuery的路径"></script> ...