首先看一下以下示例。(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的更多相关文章

  1. Bound Method and Unbound Method - 绑定方法 与 非绑定方法

    Bound Method and Unbound Method 通常有两种方法对类的方法(instance.method)/属性(class.attribute)进行引用, 一种称做 Bound Me ...

  2. [转载]Python方法绑定——Unbound/Bound method object的一些梳理

    本篇主要总结Python中绑定方法对象(Bound method object)和未绑定方法对象(Unboud method object)的区别和联系.主要目的是分清楚这两个极容易混淆的概念,顺便将 ...

  3. python tips:类的绑定方法(bound)和非绑定方法(unbound)

    类属性只有类及其实例能够访问,可以理解为一个独立的命名空间. Python中类属性的引用方式有两种: 1. 通过类的实例进行属性引用,称为绑定方法(bound method),可以理解为方法与实例绑定 ...

  4. Python OOP(2)-static method,class method and instance method

    静态方法(Static Method): 一种简单函数,符合以下要求: 1.嵌套在类中. 2.没有self参数. 特点: 1.类调用.实例调用,静态方法都不会接受自动的self参数. 2.会记录所有实 ...

  5. python --- Python中的callable 函数

    python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...

  6. Micro Python - Python for microcontrollers

    Micro Python - Python for microcontrollers MicroPython

  7. 从Scratch到Python——python turtle 一种比pygame更加简洁的实现

    从Scratch到Python--python turtle 一种比pygame更加简洁的实现 现在很多学校都开设了Scratch课程,学生可以利用Scratch创作丰富的作品,然而Scratch之后 ...

  8. 从Scratch到Python——Python生成二维码

    # Python利用pyqrcode模块生成二维码 import pyqrcode import sys number = pyqrcode.create('从Scratch到Python--Pyth ...

  9. [Python]Python 使用 for 循环的小例子

    [Python]Python 使用 for 循环的小例子: In [7]: for i in range(5): ...: print "xxxx" ...: print &quo ...

随机推荐

  1. XmlnsDefinition for a Cool Namespace Mapping

    In XAML, when you want to reference a CLR type, you have to add a namespace mapping that maps the XM ...

  2. Java调用SQL Server存储过程

    1.调用普通存储过程(1)创建存储过程CREATE Procedure [dbo].[GetContactListByName]  /*根据联系人姓名获取联系人信息*/@Name nvarchar(5 ...

  3. C#往SQLServer中插入大数据

    以前插入大数据的时候都是一条一条的插入,由于电脑配置不行,有一次17万条数据用了半个小时才插入完成,那个蛋疼啊! 前面听杨中科老师的课,发现一个很好的东西,25万条数据配置好的电脑几秒钟就完成了,那是 ...

  4. 【转】]Android实现开机自动运行程序

    有些时候,应用需要在开机时就自动运行,例如某个自动从网上更新内容的后台service.怎样实现开机自动运行的应用?在撰写本文时,联想到高焕堂先生以“Don't call me, I'll call y ...

  5. php命令行脚本 mock数据

    <?php $con = mysql_connect("192.168.1.5:3306","root","123"); if (!$ ...

  6. Eclipse导入到web项目没有run on server

    由于以前的项目都是用myeclipse开发的,现在要换成eclipse来开发.但是项目导入到eclipse中发现该项目并不是web项目,也不能部署到tomcat里面去. 现在解决了这个问题了.     ...

  7. Linux之计划任务

    计划任务特性 1. 需要在指定的某时间段运行 2. 需要将任务结果邮件通知用户 3. 单次任务及循环任务区别 Linux计划任务的实现工具 1. at工具 其只能执行一次性任务 其会自动加载部分环境变 ...

  8. Numpy 利用数组进行数据处理

    Numpy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环). 用数组表达式代替循环的做法,通常被称为矢量化.一般来说,矢量化数组运算要比等价的纯跑一趟湖南快 上一两个数量级( ...

  9. discuz X论坛技术架构 MVC结构浅析

    摘自:http://yeyuan.iteye.com/blog/930727 PS:本人刚接触discuz论坛,php水平有限,当中的理解,如有不正确之处,欢迎指出 ----------------- ...

  10. AWT控件称为重量级控件

    AWT 是Abstract Window ToolKit (抽象窗口工具包)的缩写,这个工具包提供了一套与本地图形界面进行交互的接口. AWT 中的图形函数与操作系统所提供的图形函数之间有着一一对应的 ...