9.1 准备工作
1.一些特性不会在老式类上起作用。在模块最开始放入赋值语句
__metaclass__=type 关于__metaclass__ 参见博客
2.子类化内建类(新类)
class NewStyle(object):
3.0中不用
- type是一种object, type is kind of object。即Type是object的子类。
- object是一个type,object is and instance of type。即Object是type的一个实例。
9.2 构造方法 __init__
>>> class FooBar:
def __init__(self,value=42):
self.somevar=value
>>> f=FooBar('This is aconstructor argument')
>>> f.somevar
'This is aconstructor argument'
9.2.1 重写一般方法和特殊的构造方法
>>> class Bird:
def __init__(self):
self.hungry=True
def eat(self):
if self.hungry:
print 'Aaaah'
else:
print 'No,thanks!'
>>> class SongBird(Bird):
def __init__(self):
self.sound='miaomiaomiao'
def sing(self):
print self.sound
>>> sb=SongBird()
>>> sb.sing()
miaomiaomiao
>>> sb.eat()
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
sb.eat()
File "<pyshell#1>", line 5, in eat
if self.hungry:
AttributeError: SongBird instance has no attribute 'hungry' #看吧 SongBird没有hungry特性
>>> class SongBird(Bird):
def __init__(self):
Bird.__init__(self) #直接调用类的方法
self.sound='miaomiaomiao'
def sing(self):
print self.sound
原理: 调用实例的方法时,self会自动绑定到实例上(绑定方法),而直接调用类的方法,就不会被实例绑定。然后再把当前实例作为self传给未绑定的方法(先提供方法,再绑定实例)
class SongBird(Bird):
def __init__(self):
super(SongBird,self).__init__() #3.0中也可以不带任何参数
self.sound='miaomiaomiao'
def sing(self):
print self.sound
9.3 成员访问(介绍特殊方法的使用)
class CounterList(list):
def __init__(self,*args):
super(CounterList,self).__init__(*args)#调用list的构造方法
self.counter=0 #添加了所需的特性
def __getitem__(self,index):
self.counter+=1
return super(CounterList,self).__getitem__(index)
新类中没有重写的都可以直接使用
9.5 属性(通过访问器定义的特性称为属性)
-
特性是对象内部的变量
-
对象的状态由它的特性来描述,对象的方法可以改变它的特性
-
可以直接从对象外部访问特性
python中使用属性(property)对特性进行访问和设置
-
有一些面向对象语言支持私有特性,这些特性无法从外部直接访问,需要编写getter和setter方法对这些特性进行操作
-
python不需要getter和seter方法,因为python中所有特性都是公开的,如果不放心使用直接访问对象的特性,可以为对象编写setter和getter 访问器方法,但是更好的解决办法是使用属性(property)
- python隐藏访问器的方法,让所有特性看起来一样,这种通过访问器定义的特性被称为属性
class Rectangle:
def __init__(self):
self.width=0
self.height=0
def setSize(self,size):
self.width,self.height=size #Tuple size是元组
def getSize(self):
return self.width,self.height
>>> r=Rectangle()
>>> r.width=10
>>> r.height=2
>>> r.getSize()
(10,5)
9.5.1 property函数(创建属性)见博客
size=property(getSize,setSize) #先取值,再赋值 隐藏访问器
property函数,把 访问器函数作为参数,创建了一个 属性,名字叫size。size可以访问之前还必须通过访问器操作的变量size
>>> r=Rectangle()
>>> r.width=10
>>> r.height=2
>>> r.size #先取值
(10, 2)
>>> r.size=150,100 #再赋值
>>> r.size
(150, 100)
>>>r.width
150
__metaclass__=type
class MyClass:
def smeth():
print 'This is a static method'
smeth=staticmethod(smeth) #被装入staticmethod 类型的对象中 手动
def cmeth(cls):
print 'This is a class method of',cls
cmeth=classmethod(cmeth) #被装入classmethod类型的对象中
不想手动包装和替换,就使用 装饰器 (decorator)(对任何可调用的对象进行包装,既能用于方法也能用于函数) @
__metaclass__=type
class MyClass:
@staticmethod #包装进staticmethod,可以指定多个装饰器
def smeth():
print 'This is a static method'
@classmethod
def cmeth(cls):
print 'This is a class method of',cls
使用
>>> MyClass.smeth()
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>
9.5..3 __getattr__ __setattr__ 和它的朋友
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: #如果不是调用size,把值放入实例字典,变成属性
self.__dict__[name]=value
def __getattr__(self,name):
if name =='size':
return self.width,self.height
else:
raise AttributeError
例子:
>>> c=Rectangle()
>>> c.size=(1,2)
>>> c.op=25
>>> c.op
25
>>> c.size
(1, 2)
>>> c.a
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
c.a
File "C:\Users\Mojar\Desktop\open.py", line 14, in __getattr__
raise AttributeError
AttributeError
注意:__getattribute__拦截所有特性的访问包括__dict__,访问其中与self相关特性时,使用超类的__getattr__才是安全的方法、
9.6 迭代器 __iter__
题外话:
- 内置函数iter()仅仅是调用了对象的
__iter__()
方法,所以list对象内部一定存在方法__iter__()
- 内置函数next()仅仅是调用了对象的
__next__()
方法,所以list对象内部一定不存在方法__next__()
,但是Itrator中一定存在这个方法。 -
for循环内部事实上就是先调用iter()把Iterable变成Iterator在进行循环迭代的。
1.计算一个值获取一个值,列表一次性获取所有值
class Fibs:
def __init__(self):
self.a=0
self.b=1
def next(self):
self.a,self.b=self.b,self.a+self.b #从__iter__那不断得到self,对self进行操作
return self.a
def __iter__(self): #返回本身,既迭代
return self
一般放到会在for中循环使用的对象
>>> for a in f:
if a>1000:
print a
break
1597
使用 iter()获得迭代器
>>> it=iter([1,2,3])
>>> it.next()
1
>>> it.next()
2 #如果迭代没有值可以返回,引发一个StopIteration
迭代器和可迭代对象都可以进行迭代
9.6.2 从迭代器得到序列
>>> class TestIterator:
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)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
迭代器本身不可以后退
9.7 生成器
下面为一个可以无穷生产奇数的生成器函数。
def odd():
n=1
while True:
yield n
n+=2
odd_num = odd()
count = 0
for o in odd_num:
if count >=5: break
print(o)
count +=1
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element #无return
yield 与 return
在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration;
1
2
3
4
5
6
7
8
9
10
11
|
>>>
def
g1
(
)
:
.
.
.
yield
1
.
.
.
>>>
g
=
g1
(
)
>>>
next
(
g
)
#第一次调用next(g)时,会在执行完yield语句后挂起,所以此时程序并没有执行结束。
1
>>>
next
(
g
)
#程序试图从yield语句的下一条语句开始执行,发现已经到了结尾,所以抛出StopIteration异常。
Traceback
(
most
recent
call
last
)
:
File
"<stdin>"
,
line
1
,
in
<
module
>
StopIteration
>>>
|
如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>
def
g2
(
)
:
.
.
.
yield
'a'
.
.
.
return
.
.
.
yield
'b'
.
.
.
>>>
g
=
g2
(
)
>>>
next
(
g
)
#程序停留在执行完yield 'a'语句后的位置。
'a'
>>>
next
(
g
)
#程序发现下一条语句是return,所以抛出StopIteration异常,这样yield 'b'语句永远也不会执行。
Traceback
(
most
recent
call
last
)
:
File
"<stdin>"
,
line
1
,
in
<
module
>
StopIteration
|
如果在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。
生成器没有办法使用return来返回值。
>>> nested=[[1,2,3],[3,4],[5]]
>>> for num in flatten(nested):#生成器被调用 时返回一个迭代器
print num
1
2
3
3
4
5
注:生成器是包含有__iter()和next__()方法的,所以可以用for
记得第五章说过,列表推导式把[]改成()不会得到‘元组推导式’ 而是一个生成器吗?
>>> g=((i+2)**2 for i in range(2,27))
>>> g.next()
16
9.7.2递归生成器
def flatten(nested):
try:
sublist in nested:
for element in flatten(sublist): #这就是递归所在
yield element
except TypeError: #如果展开的是一个数,展开不了,就异常,输出数
yield nested
过程:
def flatten(nested):
try:#
try nested+'' #只检查nested像不像一个字符串
except TypeError:pass #非字符串,什么都不做
else:raise TypeError #是字符串,引发异常
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
9.7.4 生成器方法
def repeater(value):
while True:
new=(yield value)
if new is not None:value =new
>>> r=repeater(45) #在内部挂起生成器
>>> r.next()
45
>>> r.next()
45
>>> r.send('hello')
'hello'
close()
手动关闭生成器函数,后面的调用会直接返回StopIteration异常。
>>> def g4():
... yield 1
... yield 2
... yield 3
...
>>> g=g4()
>>> next(g)
1
>>> g.close()
>>> next(g) #关闭后,yield 2和yield 3语句将不再起作用
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
send()
生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。
这是生成器函数最难理解的地方,也是最重要的地方,实现后面我会讲到的协程就全靠它了。
def gen():
value=0
while True:
receive=yield value
if receive=='e':
break
value = 'got: %s' % receive
g=gen() #将生成器 (迭代器)赋给对象g
print(g.send(None)) #启动生成器
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))
执行流程:
- 通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。(挂起生成器)
此时,执行完了yield语句,但是没有给receive赋值。
yield value会输出初始值0
注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。 - 通过g.send('aaa'),会传入aaa,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。
此时yield value会输出"got: aaa",然后挂起。 - 通过g.send(3),会重复第2步,最后输出结果为"got: 3"
-
当我们g.send('e')时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会得到StopIteration异常。
最后的执行结果如下:0 got: aaa got: 3 Traceback (most recent call last): File "h.py", line 14, in <module> print(g.send('e')) StopIteration
throw()
用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。
throw()后直接跑出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。
def gen():
while True:
try:
yield 'normal value'
yield 'normal value 2'
print('here')
except ValueError:
print('we got ValueError here')
except TypeError:
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))
输出结果为:
normal value
we got ValueError here
normal value
normal value 2
Traceback (most recent call last):
File "h.py", line 15, in <module>
print(g.throw(TypeError))
StopIteration
解释:
- print(next(g)):会输出normal value,并停留在yield 'normal value 2'之前。
- 由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield 'normal value 2'不会被执行,然后进入到except语句,打印出we got ValueError here。
然后再次进入到while语句部分,消耗一个yield,所以会输出normal value。 - print(next(g)),会执行yield 'normal value 2'语句,并停留在执行完该语句后的位置。
- g.throw(TypeError):会跳出try语句,从而print('here')不会被执行,然后执行break语句,跳出while循环,然后到达程序结尾,所以跑出StopIteration异常。
下面给出一个综合例子,用来把一个多维列表展开,或者说扁平化多维列表)
def flatten(nested):
try:
#如果是字符串,那么手动抛出TypeError。
if isinstance(nested, str):
raise TypeError
for sublist in nested:
#yield flatten(sublist)
for element in flatten(sublist):
#yield element
print('got:', element)
except TypeError:
#print('here')
yield nested
L=['aaadf',[1,2,3],2,4,[5,[6,[8,[9]],'ddf'],7]]
for num in flatten(L):
print(num)
如果理解起来有点困难,那么把print语句的注释打开在进行查看就比较明了了。
9.7.5 模拟生成器
9.8 八皇后问题
def conflict(state,nextX):#判断底层皇后是否和之前的冲突
nextY=len(state) #state存放之前位置信息的元组
for i in range(nextY):
if abs(state[i]-nextX) in (0,nextY-i):
return True
return False
def queens(num=8,state=()):
for pos in range(num):
if not conflict(state,pos):
if len(state)==num-1: #是最后一个皇后,遍历完成
yield (pos,)
else:
for result in queens(num,state+(pos,)): #不是,使用递归调用queens,把当前位置放到state中
yield (pos,)+result #这里不用return 是因为要返回的是一整个元组