(原文是 Python's Magical Self ,来自 http://concentricsky.com )

Python的self参数有时真让人抓狂,比如,你必须在每一个类的方法里显示定义self,然后,它会霸占不需要它们的地方。

class Foo(object):
x = 9
def __init__(self,x):
self.x = x def bar(self,y):
return self.x + y

如果你有C++,Java或其他语言的编程背景,你会觉得 __init__ 和 bar 方法里的self 看起来很多余,python不是经常吹嘘自己的简答和优雅吗,self到底有什么用?

作用域出现了

在python里,作用域是非常简单的。Python里的一切都是对象,几乎任何东西都是在对象水平的作用域里。写一个模块试试?

# test.py
def say_hi():
print 'Hi!'

你刚刚创建了一个新的带有say_hi属性的模块对象。

定义一个类?

class Foo(object):
x = 9
def __init__(self,x):
self.x = x def bar(self,y):
return self.x + y

你刚刚写了一个带有一些属性的类对象,那些属性是 x,__init__ ,还有 bar。

实例化Foo?

foo = Foo(5)

你创建了一个带有属性x,__init__ ,和bar的Foo 实例,请记住,foo的三个属性跟Foo的不一样,待会,你就会知道为什么。

上下文就是一切

把Foo拆开:

def bar(self,y):
return self.x + y class Foo(object):
x = 9
def __init__(self,x):
self.x = x
bar = bar

先不理bar的第一参数self,如果我们单单把bar看作普通的函数,那么,接下来的就很合理了。

foo = Foo(5)

print bar(foo,4) == 9
print bar(Foo,0) == 9

好像Foo.bar也可以这么做。

print Foo.bar(foo,4) == 9
print Foo.bar(Foo,0) == 9

第一行打印出结果True,但第二行就出现了类型错误(TypeError):未绑定的方法bar必须用Foo实例作为第一个参数(出现了不匹配的类型对象)。实例化一个Foo,并且修改bar,把self参数隐藏掉。

print foo.bar(foo,4) == 9
print foo.bar(foo,0) == 9

两行代码都出现 类型错误(TypeError):bar() 需要两个参数(出现了3个)。为什么是2个,而不是3个?答案即将揭晓。



绑定self

如果你查一下三个bar的类型,你会发现,他们不全都一样。

print type(bar)
# <type 'function'>
print type(Foo.bar)
# <type 'instancemethod'>
print type(foo.bar)
# <type 'instancemethod'>

把任何函数绑定到一个实例方法对象里,并把它封装在一个实例方法对象里,实例方法就会像胶水一样,粘着类、实例对象和原始的函数,最终它们都绑在一起。

print Foo.bar.im_class == Foo
print Foo.bar.im_func == bar
print Foo.bar.im_self == None
print foo.bar.im_class == Foo
print foo.bar.im_func == bar
print foo.bar.im_self == foo

可以直截了当地用python写一个实例方法类。

class myinstancemethod(object):
def __init__(self,func,cls,instance=None):
self.im_func = func
self.im_class = cls
self.im_self = instance def __call__(_self,*args,**kwargs):
args = list(args)
if _self.im_self is not None:
args.insert(0,_self.im_self) if len(args) == 0:
raise TypeError("unbound method bar() must be called with Foo instance as first argument (got nothing instead)")
elif not isinstance(args[0],_self.im_class):
raise TypeError("unbound method bar() must be called with Foo instance as first argument (got %s instead)" % type(args[0]).__name__)
else:
return _self.im_func(*args,**kwargs)

myinstancemethod 很正确地模仿了实例方法类,它跟前面的foo.bar 和Foo.bar的表现一样,除了它处理了一点类边缘情况和实例方法调用。

my_unbound(self=foo,y=4)
# TypeError: bar() got multiple values for keyword argument 'self'
Foo.bar(self=foo,y=4)
# TypeError: bar() got multiple values for keyword argument 'self' my_bound(self=foo,y=4)
# TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
foo.bar(self=foo,y=4)
# TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)

这就是为什么你能够传入bar的引用,而不是传入foo,然后调用foo.bar。

闭包

foo 是一个与Foo完全不同的野兽。Python里的任一个变量都是内存里对象的引用——对象之间都没什么不同。Foo.x,Foo.__init__ 和 Foo.bar这三个与foo.x,foo.__Init__, 和foo.bar不同,他们都指向不同的内存空间。

print Foo.x is not foo.x
print Foo.__init__ is not foo.__init__
print Foo.bar is not foo.bar

Foo 和foo 是完全不相关的实体,它们只是碰巧在适当的时候相互引用对方。





Python魔术师--self的更多相关文章

  1. 魔术师发牌问题 -- python实现

    问题描述 魔术师手中有A.2.3--J.Q.K十三张黑桃扑克牌.在表演魔术前,魔术师已经将他们按照一定的顺序叠放好(有花色的一面朝下).魔术表演过程为:一开始,魔术师数1,然后把最上面的那张牌翻过来, ...

  2. python 练习 5

    #!/usr/bin/python # -*- coding: utf-8 -*- from collections import deque def z69(): '''猜牌术(1) 魔术师,最上面 ...

  3. python study - 正则表达式

    第 7 章 正则表达式 7.1. 概览 7.2. 个案研究:街道地址 7.3. 个案研究:罗马字母 7.3.1. 校验千位数 7.3.2. 校验百位数 7.4. 使用 {n,m} 语法 7.4.1. ...

  4. Python:从入门到实践--第八章-函数-练习

    #.消息:编写一个名为display_message()的函数,它打印一个句子,指出你在本章学的是什么. #调用这个函数,确认显示的消息无误 def display_message(name): pr ...

  5. Python编程:从入门到实践(选记)

    本文参考< Python 编程:从入门到实践>一书,作者: [ 美 ] Eric Matthes 第1章 起步 1.1     搭建python环境 在不同的操作系统中, Python 存 ...

  6. 10分钟教你Python+MySQL数据库操作

    欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可! 本文介绍如何利用python来对MySQL数据库进行操作,本文将主要从以下几个方面展开介绍: 1.数据库介绍 2.MySQL数据库安装和设置 ...

  7. 使用OpenCV(C ++ / Python)进行人脸交换

    -- 图3.面部对齐.左:检测到面部标志和凸包.中:凸包上的点的Delaunay三角剖分.右:通过仿射扭曲三角形进行面部对齐. 1 人脸对齐 1.1 脸部地标检测 两个脸部的几何形状非常不同,因此我们 ...

  8. python 制作影视动画、电影特效工具

    一直觉得电影特效,动画制作这些都很什么,在google上搜索了下python开发电影特效的内容,发现了几个不错的软件,都支持python脚本开发. Houdini  Houdini (电影特效魔术师) ...

  9. python禁止函数修改列表的实现方法

    python禁止函数修改列表的实现方法 有时候,需要禁止函数修改列表.例如要对裂变进行修改操作,也要保留原来的未打印的设计列表,以供备案.为解决这个问题,可向函数传递列表的副本而不是原件:这样函数所做 ...

随机推荐

  1. 《SPFA算法的优化及应用》——姜碧野(学习笔记)

    一.核心性质:三角不等式.最短路满足d[v]<=d[u]+w(u,v) 二.SPFA两种实现: 常见的是基于bfs的,这是直接对bellman-ford用队列维护.根据最短路的长度最长为(n-1 ...

  2. UVA 11294 Wedding(2-sat)

    2-sat.不错的一道题,学到了不少. 需要注意这么几点: 1.题目中描述的是有n对夫妇,其中(n-1)对是来为余下的一对办婚礼的,所以新娘只有一位. 2.2-sat问题是根据必然性建边,比如说A与B ...

  3. UVA 821 Page Hopping 网页跳跃(BFS,简单)

    题意: 给一个无权有向图,可认为边的长度为1,求两点间的平均长度(即所有点对的长度取平均),保留3位小数.保证任意点对都可达. 思路: 简单题.直接穷举每个点,进行BFS求该点到其他点的距离.累加后除 ...

  4. 如何获取数据块结构信息dump

    有个pub_department的表,索引为PK_PUB_DEPARTMENT. 1.找到object_id select   object_id from dba_objects s  where  ...

  5. TOAD FOR MYSQL 进行数据插入时乱码的解决办法---MariaDB 5.5

    最近使用mysql是发现插入的数据乱码,几经周折终于找到的解决方法,特作备忘. 开始有将mysql的字符集全部设置成utf8,如下: SHOW VARIABLES LIKE 'character_se ...

  6. 深入学习Android有关网络连接的内容

    基于实际项目中碰到的些许问题,开始重新深入学习Android网络连接有关知识. Android的4.1.2版本的AndroidHttpClient模块setSoTimeout之后实际超时时间和设置的时 ...

  7. CentsOS7 网络自动启动

    虚拟机中安装完成CentOS7后,网络总是需要手工启动才可使用,设置为自动连接的方式如下: cd /etc/sysconfig/network-scripts/ls #找到类似的文件:ifcfg-et ...

  8. DOM笔记(二):Node接口

    所有的节点都使用Node接口来表示,可以使用很多方法去获取节点,如document.getElementsByTagName().document.getElementsByName()等均返回一个N ...

  9. SeaJS学习笔记(一) ./ 和 ../ 区别

    最近要去实习,公司里使用sea.js进行模块化开发 具体下载安装就不多说了,请参见SeaJS官网 <!DOCTYPE html> <html> <head> < ...

  10. 分析特定类的python脚本

    今天接触了下pyUSB,事先没看对象内部成员资料,直接用python的dir函数看了看pyUSB的内部构成.突然间想到自己可不可以写个简单的脚本,利用dir或其他函数遍历某个对象内部的所有成员,并打印 ...