- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议57:为什么需要 self 参数
self想必大家都不陌生,在类中当定义实例方法的时候需要将第一个参数显式声明为self,而调用的时候并不需要传入该参数。我们可以使用self.x来访问实例变量,也可以在类中使用self.m()来访问实例方法。self的使用示例如下:
class SelfTest(object): def __init__(self,name): self.name = name def showself(self): print "self here is%s"%self def display(self): self.showself() print ("The name is:",self.name) st = SelfTest("instance self") st.display() print "%X"%(id(st))
上例中我们使用self.name来表示实例变量name,在display方法中使用self.showself()来调用实例方法showself(),并且调用的时候没有显式传入self参数。程序输出如下:
self here is<__main__.SelfTest object at 0x00D67C10> ('The name is:', 'instance self') D67C10
从上述输出中可以看出,self表示的就是实例对象本身,即SelfTest类的对象在内存中的地址。self是对对象st本身的引用。我们在调用实例方法的时候也可以直接传入实例对象:如:SelfTest.display(st)。其实self本身并不是Python的关键字(cls也不是),可以将self替换成任何你喜欢的名称,如this、obj等,实际效果和self是一样的(并不推荐这样做,使用self更符合约定俗成的原则)。
也许很多人感受self最奇怪的地方就是:在方法声明的时候需要定义self作为第一个参数,而调用方法的时候却不用传入这个参数。虽然这并不影响语言本身的使用,而且也很容易遵循这个规则,但多多少少会在心里问一问:既然这样,为什么必须在定义方法的时候声明self参数呢?去掉第一个参数self不是更简洁吗?就如C++中的this指针一样。我们来简单探讨一下为什么需要self。
1)Python在当初设计的时候借鉴了其他语言的一些特征,如Moudla-3中方法会显式地在参数列表中传入self。Python起源于20世纪80年代末,那个时候的很多语言都有self,如Smalltalk、Modula-3等。Python在最开始设计的时候受到了其他语言的影响,因此借鉴了其中的一些理念(注:即使不了解Smalltalk、Modula-3也没有关系,此处只是为了说明当初在设计Python时借鉴了其他语言的一些特点)。下面这段话摘自Guido 1998年接受的一个访问,他自己也提到了这一点。
Andrew:What other languages or systems have influenced Python's design?(在Python的设计过程中受到了哪些语言或者系统的影响?)
Guido:There have been many.ABC was a major influence,of course,since I had been working on it at CWI.It inspired the use of indentation to delimit blocks,which are the high-level types and parts of object implementation.I'd spent a summer at DEC's Systems Research Center,where I was introduced to Modula-2+;theModula-3final report was being written there at about the same time. What I learned there showed up in Python's exception handling,modules,and the fact that methods explicitly contain “self” in their parameter list.String slicing came from Algol-68 and Icon.(有很多,当然,ABC影响最大,因为在CWI的时候我一直在研究它。它启发了我使用缩进来分块,这些是高级的类型以及部分对象的实现。我在DEC的系统研究中心花费了一个暑假的时间,在那里我学到了Modula-2+。而Modula-3也是在同一时期在那里被实现的。我在那里学到的,在Python的异常处理、模块以及方法的参数列表中显式包含self中都有体现,而字符串的分隔则是从Algol-68和Icon中借鉴的。)
2)Python语言本身的动态性决定了使用self能够带来一定便利。下例中len表示求点到原点距离的函数,现在有表示直角三角形的类Rtriangle,我们发现求第三边边长和len所实现的功能其实是一样的,所以打算直接重用该方法。由于self在函数调用中是隐式传递的,因此当直接调用全局函数len()时传入的是point对象,而当在类中调用该方法时,传入的是类所对应的对象,使用self可以在调用的时候决定应该传入哪一个对象。
>>> def len(point): ... return math.sqrt(point.x ** 2 + point.y ** 2) ... >>> class RTriangle(object): ... def __init__(self,right_angle_sideX,right_angle_sideY): ... self.right_angle_sideX = right_angle_sideX ... self.right_angle_sideY = right_angle_sideY ... >>> RTriangle.len = len >>> rt = RTriangle(3,4) >>> rt.len() 5.0 >>>
Python属于一级对象语言(first class object),如果m是类A的一个方法,有好几种方式都可以引用该方法,如下例所示:
>>> class A: ... def m(self,value): ... pass ... >>> A.__dict__['m'] <function m at 0x00D617B0> >>> A.m.__func__ <function m at 0x00D617B0>
实例方法是作用于对象的,如果用户使用上述两种形式来调用方法,最简单的方式就是将对象本身传递到该方法中去,self的存在保证了A.__dict__['m'](a,2)的使用和a.(2)一致。同时当子类覆盖了父类中的方法但仍然想调用该父类的方法的时候,可以方便地使用baseclass.methodname ( self, <argument list> )或super ( childclass, self ) .methodname ( < argument list > )来实现。
3)在存在同名的局部变量以及实例变量的情况下使用self使得实例变量更容易被区分。
value = 'default global' class Test(object): def __init__(self,par1): value = par1+"------" self.value = value def show(self): print self.value print value a = Test("instance") show()
上述程序中第一个value为全局变量,第二个为局部变量,仅仅在方法中可见,而类同时定义了一个实例变量value。如果没有self,我们很难区分到底哪个表示局部变量,哪个表示实例变量,有了self这一切一目了然了。
其实关于要不要self或者要不要将self作为关键字一直是一个很有争议的问题,也有人提了一些修正建议。但Guido认为,基于Python目前的一些特性(如类中动态添加方法,在类风格的装饰器中没有self无法确认是返回一个静态方法还是类方法等)保留其原有设计是个更好的选择,更何况Python的哲学是:显式优于隐式(Explicit is better than implicit.)。个人体会是除了在定义的时候多输入几个字符外,self的存在并不会给用户带来太多困扰,我们没有必要过分纠结于它是否应该存在这个问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论