第九章 魔法方法 属性和迭代器
构造方法,示例代码如下:
>>>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 aconstructor argument'
重写一般方法和特殊的构造方法
>>>class A:
def hello(self):
print ('hello ,i am A')
>>>class B(A):
pass
>>>a=A()
>>>b=B()
>>>a.hello()
hello ,i am A
>>>b.hello()
hello ,i am A
#在B中重写A 类的方法
>>>class B(A):
def hello(self):
print('hello,i am B')
>>>b=B()
>>>b.hello()
hello,i am B
调用超类的构造方法:
A 调用未绑定的超类的构造方法
B 使用super函数
重写构造方法后会出现如下情况:
class Bird:
def __init__(self):
self.hungry=True
def eat(self):
if self.hungry:
print('Aaaah...')
self.hungry=False
else:
print('no,thanks')
>>>b=Bird()
>>>b.eat()
Aaaah...
>>>b.eat()
no,thanks
>>>
>>>class SongBird():
def __init__(self):
self.sound='squawk!'
def sing(self):
print(self.sound)
class SongBird(Bird):
def __init__(self):
self.sound='squawk!'
def sing(self):
print(self.sound)
>>>sb=SongBird()
>>>sb.sing()
squawk!
#子类在调用父类方法时,没有初始化hungry属性
>>>sb.eat()
Traceback (mostrecent call last):
File "<pyshell#78>", line 1,in <module>
sb.eat()
File "<pyshell#59>", line 5,in eat
if self.hungry:
AttributeError:'SongBird' object has no attribute 'hungry'
解决方法:
A 调用未绑定的超类的构造方法
>>>class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound='squawk!'
def sing(self):
print(self.sound)
>>>sb=SongBird()
>>>sb.sing()
squawk!
>>>sb.eat()
Aaaah...
>>>sb.eat()
no,thanks
B 使用super函数
classSongBird(Bird):
def __init__(self):
super(SongBird,self).__init__()
self.sound='squawk!'
def sing(self):
print(self.sound)
>>>sb=SongBird()
>>>sb.sing()
squawk!
>>>sb.eat()
Aaaah...
>>>sb.eat()
no,thanks
访问成员
基本的序列和映射规则
可变序列的四个方法
__len__(self):
__getitem__(self,key):
__setitem__(self,key,value):
__delitem__(self,key):
def checkIndex(key):
"""
所给的键是不是参接受的索引
键应该是一个非负的整数
"""
if not isinstance(key,int):
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):
"""
get an item form the arithmeticsequence
"""
checkIndex(key)
try:
return self.changed[key]
except KeyError:
returnself.start+key*self.step
def __setitem__(self,key,value):
"""
修改算术序列中的一个项
"""
checkIndex(key)
self.changed[key]=value #保存修改后的值
>>>s=ArithmeticSequence(1,2)
>>>s[0]
1
>>>s[4]
9
子类化列表字典和字符串
#和内建列表行为相似的序列,可以使用list
#带有访问计数的列表
class CounterList(list):
def __init__(self,*args):
super(CounterList,self).__init__(*args)
self.counter=0
def __getitem__(self,index):
self.counter+=1
returnsuper(CounterList,self).__getitem__(index)
>>>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]
>>> delc1[3:6]
>>> c1
[9, 8, 7, 3, 2,1, 0]
>>>c1.counter
0
>>>c1[4]+c1[2]
9
>>>c1.counter
2
属性
class Rectangle:
def __init__(self):
self.width=0
self.height=0
def setSize(self,size):
self.width,self.height=size
def getSize(self):
return self.width,self.height
>>>r=Rectangle()
>>>r.width=10
>>>r.height=5
>>>r.getSize()
(10, 5)
>>>r.setSize((100,150))
>>>r.width
100
Proterty函数
四个参数分别为:
Fget
Fset
Fdel
doc
__metaclass__=type
class Rectangle:
def __init__(self):
self.width=0
self.height=0
def setSize(self,size):
self.width,self.height=size
def getSize(self):
return self.width,self.height
size=property(getSize,setSize)
>>>r.width=10
>>>r.height=5
>>>r.size
(10, 5)
>>>r.size=100,150
>>>r.size
(100, 150)
静态方法和类成员方法
Static method 没有self参数,且能够被类本身调用,
Class method 需要用cls的参数
#方式一手动包装
__metaclass__=type
class Myclass:
def smeth():
print ('this is a static method')
#声明静态方法
smeth=staticmethod(smeth)
def cmeth(cls):
print('this is a class method of',cls)
#声明类方法
cmeth=classmethod(cmeth)
>>>Myclass.smeth()
this is a staticmethod
>>>Myclass.cmeth()
this is a classmethod of <class '__main__.Myclass'>
#方式二 使用注解的方式
__metaclass__=type
class Myclass:
@staticmethod
def smeth():
print ('this is a static method')
@classmethod
def cmeth(cls):
print('this is a class methodof',cls)
>>>Myclass.smeth()
this is a staticmethod
>>>Myclass.cmeth()
this is a classmethod of <class '__main__.Myclass'>
__getattr__ __setattr__ 和它的朋友们
有点类似于java中的get和set方法,不同的是不用每个属性都去编写
class Rectangle:
def __init__(self):
self.width=0
self.height=0
def __setattr__(self,name,value):
if name=='size':
self.width,self.height=value
else:
self.__dict__[name]=value
def __getattr__(self,name):
if name=='size':
returnself.width,self.height
else:
raise AttributeError
迭代器
__iter__
迭代器的规则
#测试不成功
class Fibs:
def __init__(self):
self.a=0
self.b=1
def next(self):
self.a,self.b=self.b,self.a+self.b
return self.a
def __iter__(self):
return self
>>>fibs=Fibs()
>>> forf in fibs:
if f > 1000:
print(f)
break
Traceback (mostrecent call last):
File "<pyshell#15>", line 1,in <module>
for f in fibs:
TypeError:iter() returned non-iterator of type 'Fibs'
>>> it=iter([1,2,3])
#3.0 之前是使用it.next()
>>>next(it)
1
从迭代器得到序列 ????????
classTestIterator:
value=0
def next(self):
self.value+=1
if self.value>10:
raise StopIteration
return self.value
def __iter__(self):
return self
>>>ti=TestIterator()
>>>list(ti)
Traceback (mostrecent call last):
File "<pyshell#34>", line 1,in <module>
list(ti)
TypeError:iter() returned non-iterator of type 'TestIterator'
生成器
创建生成器
defflatten(nested):
for sublist in nested:
for element in sublist:
yield element
>>>mested=[[1,2],[3,4],[5]]
>>> fornum in flatten(mested):
print(num)
递归生成器
如果要处理任意层的嵌套
defflatten(nested):
try:
#不要迭代类似于字符串的对象
try:
nested+''
except TypeError:pass
else:
raise TypeError
for sublist in nested:
for element inflatten(sublist):
yield element
except TypeError:
yield nested
list(flatten([[[1],2],3,4,[5,[6,7]],8]))
[1, 2, 3, 4, 5,6, 7, 8]
通用生成器
生成器的方法
是生成器与外界交流的渠道
使用send方法就像使用next(),需要一个参数(即要发送的消息)
Send()方法只有在生成器挂起后才有意义
>>> def repeater(value):
while True:
new=(yield value)
if new is notNone:
value=new
>>> r=repeater(42)
>>> r.next()
Traceback (most recent call last):
File"<pyshell#42>", line 1, in <module>
r.next()
AttributeError: 'generator' object has no attribute 'next'
>>> r.send('hello world!')
Traceback (most recent call last):
File"<pyshell#43>", line 1, in <module>
r.send('hello world!')
TypeError: can't send non-None value to a just-started generator
在2.5以后的版本中,还有两人方法:
Throw()
Close()
模拟生成器
八皇后问题
寻找下一个没有冲突的位置
>>> defconflict(state,nextX):
nextY=len(state)
for i in range(nextY):
if abs(state[i]-nextX) in(0,nextY-i):
return True
return False
#基本情况
>>> defqueens(num,state):
if len(state)==num-1:
for pos in range(num):
if not conflict(state,pos):
yield pos
>>>list(queens(4,(1,3,0)))
[2]
需要递归的情况
defqueens(num=8,state=()):
for pos in range(num):
if not conflict(state,pos):
if len(state)==num-1:
yield (pos,)
else:
for result inqueens(num,state+(pos,)):
yield(pos,)+result
for solution inqueens(8):
print(solution)
(0, 4, 7, 5, 2,6, 1, 3)
(0, 5, 7, 2, 6,3, 1, 4)
(0, 6, 3, 5, 7,1, 4, 2)
(0, 6, 4, 7, 1,3, 5, 2)
(1, 3, 5, 7, 2,0, 6, 4)
(1, 4, 6, 0, 2,7, 5, 3)
(1, 4, 6, 3, 0,7, 5, 2)
(1, 5, 0, 6, 3,7, 2, 4)
(1, 5, 7, 2, 0,3, 6, 4)
(1, 6, 2, 5, 7,4, 0, 3)
(1, 6, 4, 7, 0,3, 5, 2)
(1, 7, 5, 0, 2,4, 6, 3)
(2, 0, 6, 4, 7,1, 3, 5)
(2, 4, 1, 7, 0,6, 3, 5)
(2, 4, 1, 7, 5,3, 6, 0)
(2, 4, 6, 0, 3,1, 7, 5)
(2, 4, 7, 3, 0,6, 1, 5)
(2, 5, 1, 4, 7, 0,6, 3)
(2, 5, 1, 6, 0,3, 7, 4)
(2, 5, 1, 6, 4,0, 7, 3)
(2, 5, 3, 0, 7,4, 6, 1)
(2, 5, 3, 1, 7,4, 6, 0)
(2, 5, 7, 0, 3,6, 4, 1)
(2, 5, 7, 0, 4,6, 1, 3)
(2, 5, 7, 1, 3,0, 6, 4)
(2, 6, 1, 7, 4,0, 3, 5)
(2, 6, 1, 7, 5,3, 0, 4)
(2, 7, 3, 6, 0,5, 1, 4)
(3, 0, 4, 7, 1,6, 2, 5)
(3, 0, 4, 7, 5,2, 6, 1)
(3, 1, 4, 7, 5,0, 2, 6)
(3, 1, 6, 2, 5,7, 0, 4)
(3, 1, 6, 2, 5,7, 4, 0)
(3, 1, 6, 4, 0,7, 5, 2)
(3, 1, 7, 4, 6,0, 2, 5)
(3, 1, 7, 5, 0,2, 4, 6)
(3, 5, 0, 4, 1,7, 2, 6)
(3, 5, 7, 1, 6,0, 2, 4)
(3, 5, 7, 2, 0,6, 4, 1)
(3, 6, 0, 7, 4,1, 5, 2)
(3, 6, 2, 7, 1,4, 0, 5)
(3, 6, 4, 1, 5,0, 2, 7)
(3, 6, 4, 2, 0,5, 7, 1)
(3, 7, 0, 2, 5,1, 6, 4)
(3, 7, 0, 4, 6,1, 5, 2)
(3, 7, 4, 2, 0,6, 1, 5)
(4, 0, 3, 5, 7,1, 6, 2)
(4, 0, 7, 3, 1,6, 2, 5)
(4, 0, 7, 5, 2,6, 1, 3)
(4, 1, 3, 5, 7,2, 0, 6)
(4, 1, 3, 6, 2,7, 5, 0)
(4, 1, 5, 0, 6,3, 7, 2)
(4, 1, 7, 0, 3,6, 2, 5)
(4, 2, 0, 5, 7,1, 3, 6)
(4, 2, 0, 6, 1,7, 5, 3)
(4, 2, 7, 3, 6,0, 5, 1)
(4, 6, 0, 2, 7,5, 3, 1)
(4, 6, 0, 3, 1,7, 5, 2)
(4, 6, 1, 3, 7,0, 2, 5)
(4, 6, 1, 5, 2,0, 3, 7)
(4, 6, 1, 5, 2,0, 7, 3)
(4, 6, 3, 0, 2,7, 5, 1)
(4, 7, 3, 0, 2,5, 1, 6)
(4, 7, 3, 0, 6,1, 5, 2)
(5, 0, 4, 1, 7,2, 6, 3)
(5, 1, 6, 0, 2,4, 7, 3)
(5, 1, 6, 0, 3,7, 4, 2)
(5, 2, 0, 6, 4,7, 1, 3)
(5, 2, 0, 7, 3,1, 6, 4)
(5, 2, 0, 7, 4,1, 3, 6)
(5, 2, 4, 6, 0,3, 1, 7)
(5, 2, 4, 7, 0,3, 1, 6)
(5, 2, 6, 1, 3,7, 0, 4)
(5, 2, 6, 1, 7,4, 0, 3)
(5, 2, 6, 3, 0,7, 1, 4)
(5, 3, 0, 4, 7,1, 6, 2)
(5, 3, 1, 7, 4,6, 0, 2)
(5, 3, 6, 0, 2,4, 1, 7)
(5, 3, 6, 0, 7,1, 4, 2)
(5, 7, 1, 3, 0,6, 4, 2)
(6, 0, 2, 7, 5,3, 1, 4)
(6, 1, 3, 0, 7,4, 2, 5)
(6, 1, 5, 2, 0,3, 7, 4)
(6, 2, 0, 5, 7,4, 1, 3)
(6, 2, 7, 1, 4,0, 5, 3)
(6, 3, 1, 4, 7,0, 2, 5)
(6, 3, 1, 7, 5,0, 2, 4)
(6, 4, 2, 0, 5,7, 1, 3)
(7, 1, 3, 0, 6,4, 2, 5)
(7, 1, 4, 2, 0,6, 3, 5)
(7, 2, 0, 5, 1,4, 6, 3)
(7, 3, 0, 2, 5,1, 6, 4)
>>> defprettyprint(solution):
def line(pos,length=len(solution)):
return '. '*(pos)+'X '+'.'*(length-pos-1)
for pos in solution:
print (line(pos))
>>>import random
>>> prettyprint(random.choice(list(queens(8))))
. . X . . . . .
. . . . . X . .
. . . . . . . X
X . . . . . . .
. . . X . . . .
. . . . . . X .
. . . . X . . .
. X . . . . . .