[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 ...
随机推荐
- Sanjeev Arora
中文名:桑吉弗 阿罗拉 个人主页:https://www.cs.princeton.edu/~arora/ 生日:1968年一月 祖籍:印度 本科:1990年麻省理工大学毕业 博士:1994年加州伯克 ...
- VS自带的dbghelp.h文件 报错
场景: 编译报错: 解决方法: 在#include <dbghelp.h> 之前 #include <Windows.h>
- Lucene系列一:搜索引擎核心理论
一.为什么需要搜索引擎 问题1:数据库索引的原理是怎样的? 索引原理:对列值创建排序存储,数据结构={列值.行地址}.在有序数据列表中就可以利用二分查找快速找到要查找的行的地址,再根据地址直接取行数据 ...
- 使用@Ignore注解
断续上一节的例子,了解如何使用@Ignore注解.在测试类FirstDayAtSchoolTest中,我们将添加@Ignore注解到testAddPencils()方法.以这种方式,我们期望这个测试方 ...
- Maven外部依赖
正如大家所了解的那样,Maven确实使用 Maven 库的概念作依赖管理.但是,如果依赖是在远程存储库和中央存储库不提供那会怎么样? Maven 提供为使用外部依赖的概念,就是应用在这样的场景中的. ...
- Eclipse和MyEclipse的区别 分类: 编程工具 2015-07-18 11:12 23人阅读 评论(0) 收藏
今天,在一个Q群里有人问Eclipse和MyEclipse的区别.虽然对于知道的人来说答案很简单,但是对于不知道的人来说就很难,很多问题也都是这样的,会者不难,难者不会. 其实,网上搜搜答案就挺多的, ...
- Linux 文件类型及操作
一. 文件类型 1.Linux文件类型如下图所示: 2.Linux文件类型有许多种,不同的文件类型代表特殊意义,使用以下命令可以查看文件类型: [root@VMredhat6 ~]# ls -l ...
- php 统计fasta 序列长度和GC含量
最近php7的消息铺天盖地, 忍不住想尝试下.星期天看了下语法, 写个小脚本练下手: 这个脚本读取fasta 文件, 输出序列的长度和GC含量: <?php $fasta = "tes ...
- mysql分页查询语句怎么写?
ref: http://www.dashen100.com/question/500 是用limit函数 取前5条数据 select * from table_name limit 0,5 或者 se ...
- asp.net单击头模板中的checkbox,实现datalist中所有chebox的全选和取消
转载时请以超链接形式标明文章原始出处和作者信息及本声明http://blueseach.blogbus.com/logs/31281126.html 使用C#和javascript都可以实现,第二种更 ...