前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Python基础教程》 读书笔记 第九章 魔法方法、属性和迭代器 (上)

《Python基础教程》 读书笔记 第九章 魔法方法、属性和迭代器 (上)

作者头像
统计学家
发布2019-04-10 16:19:21
4990
发布2019-04-10 16:19:21
举报

构造方法

在Python中创建一个构造方法很容易。只要把init方法的名字从简单的init修改为魔法版本__init__即可:

>>> class FooBar: ... def __init__(self): ... self.somevar=42 ... >>> f=FooBar() >>> f.somevar 42

给构造方法传几个参数

>>> class FooBar: ... def __init__(self,value=42): ... self.somevar=value ... >>> f=FooBar('this is a constructor argument') >>> f.somevar 'this is a constructor argument'

重写一般方法和特殊的构造方法

构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类(你所继承的类)的构造方法,否则对象可能不会被正确地初始化。

>>> class Bird: ... def __init__(self): ... self.hungry=True ... def eat(self): ... if self.hungry: ... print 'aaah...' ... self.hungry=False ... else: ... print 'no thanks' ... >>> b=Bird() >>> b.eat() aaah... >>> b.eat() no thanks

鸟吃过了以后,它就不再饥饿。现在考虑子类SongBird,它添加了唱歌的行为:

>>> class SongBird(Bird): ... def __init__(self): ... self.sound='Squawk' ... def sing(self): ... print self.sound ... >>> s=SongBird() >>> s.sing() Squawk

因为SongBird是Bird的一个子类,它继承了eat方法,但如果调用eat方法,就会产生一个问题:

>>> s.eat() Traceback (most recent call last): File "<input>", line 1, in <module> File "<input>", line 5, in eat AttributeError: SongBird instance has no attribute 'hungry'

错误:SongBird没有hungry特性。原因是这样的:在SongBird中,构造方法被重写,但新的构造方法没有任何关于初始化hungry特性的代码。为了达到预期的效果,SongBird的构造方法必须调用其超类Bird的构造方法来确保进行基本的初始化。有两种方法能达到这个目的:调用超类构造方法的未绑定版本,或者使用super函数。

调用未绑定的超类构造方法

>>> class SongBird(Bird): ... def __init__(self): ... Bird.__init__(self) ... self.sound='Squawk' ... def sing(self): ... print self.sound ... >>> s=SongBird() >>> s.eat() aaah... >>> s.eat() no thanks >>> s.sing() Squawk

在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(这称为绑定方法)。但如果直接调用类的方法(比如Bird.__init__),那么就没有实例会被绑定。这样就可以自由地提供需要的self参数。这样的方法称为未绑定(unbound)方法

使用super函数

当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。

>>> __metaclass__=type >>> class Bird: ... def __init__(self): ... self.hungry=True ... def eat(self): ... if self.hungry: ... print 'aaah...' ... self.hungry=False ... else: ... print 'no thanks' ... >>> class SongBird(Bird): ... def __init__(self): ... super(SongBird,self).__init__() ... self.sound='Squawk' ... def sing(self): ... print self.sound ...

这个新式的版本的运行结果和旧式版本的一样

>>> s=SongBird() >>> s.sing() Squawk >>> s.eat() aaah... >>> s.eat() no thanks

基本的序列和映射规则

序列和映射是对象的集合。为了实现它们基本的行为(规则),如果对象是不可变的,那么就需要使用两个魔法方法,如果是可变的则需要使用4个。

__len__(self):这个方法应该返回集合中所含项目的数量。对于序列来说,这就是元素的个数。对于映射来说,则是键-值对的数量。

__getitem__(self,key):这个方法返回与所给键对应的值。对于一个序列,键应该是1个0~n-1的整数(或者像后面所说的负数),n是序列的长度;对于映射来说,可以使用任何种类的键。

__setitem__(self,key,value):这个方法应该按一定的方式存储和key相关的value,该值随后可使用__getitem__来获取。当然,只能为可以修改的对象定义这个方法。

__delitem__(self,key):这个方法在对一部分对象使用del语句时被调用,同时必须删除和元素相关的键。这个方法也是为可修改的对象定义的(并不是删除全部的对象,而只删除一些需要移除的元素)。

>>> def checkIndex(key): ... if not isinstance(key,(int,long)):raise TypeError ... if key<0:raise IndexError ... >>> class ArithmeticSequence: ... def __init__(self,start=0,step=1): ... self.start=start ... self.step=step ... self.changed={} ... def __getitem__(self,key): ... checkIndex(key) ... try:return self.changed[key] ... except KeyError: ... return self.start+key*self.step ... def __setitem__(self,key,value): ... checkIndex(key) ... self.changed[key]=value ... >>> s=ArithmeticSequence(1,2) >>> s[4] 9 >>> s[4]=2 >>> s[4] 2 >>> s[5] 11

没有实现__del__方法的原因是希望删除元素是非法的:

>>> del s[4] Traceback (most recent call last): File "<input>", line 1, in <module> AttributeError: ArithmeticSequence instance has no attribute '__delitem__'

这个类没有__len__方法,因为它是无限长的。

如果使用了一个非法类型的索引,就会引发TypeError异常,如果索引的类型是正确的但超出了范围(在本例中为负数),则会引发IndexError异常:

>>> s[four] Traceback (most recent call last): File "<input>", line 1, in <module> NameError: name 'four' is not defined >>> s[-4] Traceback (most recent call last): File "<input>", line 1, in <module> File "<input>", line 7, in __getitem__ File "<input>", line 3, in checkIndex IndexError

子类化列表,字典和字符串

例子----带有访问计数的列表:

>>> class CounterList(list): ... def __init__(self,*args): ... super(CounterList,self).__init__(*args) ... self.counter=0 ... def __getitem__(self,index): ... self.counter +=1 ... return super(CounterList,self).__getitem__(index) ...

CounterList类严重依赖于它的子类化超类(list)的行为CounterList类没有重写任何的方法(和append extend, index一样)都能被直接使用。在两个被重写的方法中,super方法被用来调用相应的超类的方法,只在__init__中添加了所需的初始化counter特性的行为,并在__getitem__中更新了counter特性。

>>> c1=CounterList(range(10)) >>> c1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> c1.reverse() >>> c1 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> del c1[3:6] >>> c1 [9, 8, 7, 3, 2, 1, 0] >>> c1[4]+c1[2] 9 >>> c1.counter 2

CounterList在很多方面和列表的作用一样,但它有一个counter特性(被初始化为0),每次列表元素被访问时,它都会自增,所以在执行加法c1[4]+c1[2〕后,这个值自增两次,变为2.

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2015-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器学习与统计学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档