[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 ...
随机推荐
- FPGA内部动态可重置PLL讲解(二)
对于全局时钟的管理,涉及到关于亚稳态的知识,大家可以上网搜索相关资料,这里不再赘述.亚稳态最简单的理解形式是无法判断是处于高电平状态还是处于低电平状态,这样会导致整个系统不稳定,会出现逻辑上的错误. ...
- getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()区别详解
getDimension()是基于当前DisplayMetrics进行转换,获取指定资源id对应的尺寸.文档里并没说这里返回的就是像素,要注意这个函数的返回值是float,像素肯定是int. getD ...
- SpringCloud微服务部署
https://blog.csdn.net/weixin_36397925/article/details/79496657 https://blog.csdn.net/forezp/article/ ...
- selenium+java-查找页面中包含关键字的URL
package seleniumLearn1; import java.io.File; import java.io.FileOutputStream; import java.io.IOExcep ...
- Android Studio 出现 Build gradle project info
导入Android Studio,一直停留在Build gradle project info.主要是因为google被墙,下载gradle很慢,有时候设置下载不成功. 参考链接 http://blo ...
- python程序中的调试:pdb
python中,也有像c/c++语言的 gdb 一样的调试程序,即pdb: 只简单说明一个怎么使用它. 假设已经有了一个python程序名为 test.py, 我们在命令行输入以下内容,即可以进行 p ...
- (原)使用android studio ndk开发流程
先使用android stuido创建一个app工程,创建工程的时候,.gradle目录结构下为2.8目录.(note:2.10目录为后续更新结果出现.) 依次修改上述红色方框标注部分内容: 1)修改 ...
- Lemon OA第4篇:常用功能
OA,Office Automation的简写,中文意思办公自动化,不同的人有不同的见解,可以简单的理解为网络化办公,高效.协同是其显著的特点,如今正在朝着智能的方向发展 平时不擅长文字,写出来几句话 ...
- Python 判断文件是否存在的三种方法
通常在读写文件之前,需要判断文件或目录是否存在,不然某些处理方法可能会使程序出错.所以最好在做任何操作之前,先判断文件是否存在. 这里将介绍三种判断文件或文件夹是否存在的方法,分别使用os模块.Try ...
- MySql折腾小记二:text/blog类型不允许设置默认值,不允许存在两个CURRENT_TIMESTAMP
From: http://www.cnblogs.com/cyq1162/archive/2011/05/17/2049055.html 在 CYQ.Data 数据框架的反向工程中,遇到MySQL的问 ...