第九章 属性 方法和迭代器
创建一个构造方法
>>> 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 argumnet')
>>> f.somevar
'This is a constructor argumnet'
重写一般方法和特殊的构造方法
>>> class A:
def hello(self):
print("hello, I'm A.")
>>> class B(A):
pass
>>> a=A() #创建类A的对象
>>> b=B() #创建类B的对象
>>> a.hello() #调用自己的hello方法
hello, I'm A.
>>> b.hello() #调用超类A的hello方法
hello, I'm A.
>>> class B(A):
def hello(self): #B类重写A类的hello方法
print("hello, I'm B.")
>>> b=B()
>>> b.hello()
hello, I'm B.
>>> 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(Bird):
def __init__(self):
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
>>> sb = SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat() # SongBird没有hungry特性,所以子类调用eat()方法出现错误
Traceback (most recent call last):
File "<pyshell#53>", line 1, in <module>
sb.eat()
File "<pyshell#41>", line 5, in eat
if self.hungry:
AttributeError: 'SongBird' object has no attribute 'hungry'
解决上面出错的办法:(1)调用超类构造方法的未绑定版本;(2)使用super函数
(1)调用未绑定的超类构造方法:Bird.__init__(self)
>>> class SongBird(Bird):
def __init__(self):
Bird.__init__(self) #未绑定方法,当前的实例作为self参数提供未绑定方法,SongBird能够使用其超类的构造方法的所有实现,属性hungry能被设置
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
>>> sb = SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()
Aaaah...
>>> sb.eat()
No, thanks!
>>>
(2)使用super函数 super(SongBird, self).__init__()
当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法
直接使用super(SongBird,self)
>>> 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!
>>>
>>> __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__() #super函数,子类就会调用超类的属性和方法
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
>>> sb = SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()
Aaah...
>>> sb.eat()
No, thanks!
2、成员访问
2.1 基本的序列和映射规则
序列和映射是对象的集合,对象不可变,需使用2个魔法方法;对象可变需使用4个
(1)__len_(self):这个方法返回集合中所含项目的数量。对于序列就是元素的个数;对于映射则是键-值对的数量。
(2)__getitem__(self,key):这个方法返回与所给键对应的值。对于序列:键应该是一个0~n-1的整数,n是序列的长度;对于映射:可以使用任何种类的键。
(3)__setitem__(self,key,value):这个方法按一定的方式存储和key相关的value,该值随后可使用__getitem__来获取。
(4)__delitem__(self,key):对一部分对象使用del语句是被调用,同时必须删除和元素相关的键;可修改对象定义的(并不是删除全部的对象,而只删除一些需要移除的元素)
3、子类化列表,字典和字符串
>>> class CounterList(list):
def __init__(self,*args):
super(CounterList,self).__init__(*args) #super方法被用来调用相应的超类的方法,只在__init__中添加了所需的初始化counter特性的行为,并在__getitem__中更新了counter特性
self.counter = 0
def __getitem__(self,index):
self.counter += 1
return super(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]
>>> del c1[3:6]
>>> c1
[9, 8, 7, 3, 2, 1, 0]
>>> c1.counter
0
4、属性:通过访问器定义的特性称为属性
>>> class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size):
self.width,self.height=size #size是由width和height构成的元组
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
4.1 property函数
>>> __metaclass__=type #子类化object,或者使用__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) #property函数创建一个属性,访问器函数被用作参数(先取值,后赋值),属性命名为size
>>> r=Rectangle()
>>> r.width =10
>>> r.height = 5
>>> r.size
(10, 5)
>>> r.size = 150,100 #size特性仍然取决于getSize和setSize中的计算
>>> r.width
150
property函数可以用0,1,2,3或者4个参数来调用。
property的4个参数分别被叫做fget,fset,fdel和doc--
在新式类中应该使用property函数而不是访问器方法。
4.2静态放法和类成员方法
静态方法:定义没有self参数,并且能够被类本身直接调用
类成员方法:定义时需要名为cls的类似于self的参数,类成员方法可以直接用类的具体对象调用,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)
装饰器:使用@操作符
使用装饰器的版本
>>> __metaclass__ = type
>>> class MyClass:
@staticmethod
def smeth():
print('This is a static method')
@classmethod
def cmeth(cls):
print('This is a class method of',cls)
>>> MyClass.smeth() #静态方法:定义没有self参数,并且能够被类本身直接调用
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>
4.3 __getattr__、__setattr__和它的朋友们
拦截对象的所有特性访问是可能的
魔法方法(可以对处理很多属性的方法进行再编码)
(1)__getattribute__(self,name):当特性name被访问时自动被调用(只能在新式类中使用)
(2)__getattr__(self,name):当特性name被访问且对象没有相应的特性时被自动调用。
(3)__setattr__(self,name,value):当试图给特性name赋值时会被自动调用。
(4)__delattr__(self,name):当试图删除特性name时被自动调用。
>>> 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 #特殊方法__dict__,该方法包含一个字典,字典里面是所有实例的属性,为避免__setattr__方法被再次调用(这样程序陷入死循环),__dict__方法被用来代替普通的特性赋值操作。
def __getattr__(self,name):
if name == 'size':
return self.width,self.height
else:
raise AttributeError
5、迭代器
特殊方法:__iter__,这个方法是迭代器规则的基础
5.1 迭代器规则:
迭代:重复做一些事很多次
__iter__方法返回一个迭代器,迭代器具有next方法(这个方法在调用时不需要任何参数)的对象,在调用next方法时,迭代器会返回它的下一个值。
一个实现了__iter__方法的对象是可迭代的,一个实现了next方法的对象则是迭代器。
>>> 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): #迭代器实现了__iter__方法,返回迭代器本身
return self
>>>
>>> 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()
>>> for f in fibs:
if f>1000:
print(f)
break
#出现该错误的原因是def next(self):需要修改成def __next__(self):
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
for f in fibs:
TypeError: iter() returned non-iterator of type 'Fibs'
正确的版本:
>>> 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() #产生一个Fibs对象
>>> for f in fibs: #在for循环中使用该对象
if f >1000:
print(f)
break
1597
内建函数iter可以从可迭代的对象中获得迭代器
>>> it = iter([1,2,3])
>>> it.__next__() #python3.0版本用__next__()方法
1
>>> it.__next__()
2
>>> it.__next__()
3
5.2 从迭代器得到序列
使用list构造方法显示地将迭代器转化为列表
>>> class TestIterator:
value = 0
def __next__(self): #此处是python3.0的版本,3.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]
6、生成器
6.1 创建生成器(处理两层嵌套)
功能:顺序打印出列表中的数字
>>> nested=[[1,2],[3,4],[5]] #列表的列表
>>> def flatten(nested):
for sublist in nested:
for element in sublist:
yield element #包含yield语句的函数称为生成器
>>> for num in flatten(nested):
print(num)
1
2
3
4
5
6.2 递归生成器(处理树形结构)
>>> def flatten(nested):
try:
for sublist in nested:
for element in flatten(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]
#类似字符串对象,在生成器的开始处添加一个检查语句,将传入的对象和一个字符串拼接
>>> def flatten(nested):
try:
# 不要迭代类似字符串的对象:
try: nested + ''
except TypeError:
pass
else:
raise TypeError
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
>>> list(flatten(['foo',['bar',['baz']]]))
['foo', 'bar', 'baz']
6.3 通用生成器
生成器是一个包含yield关键字的函数,当它被调用时,在函数体中的代码不会被执行,而返回一个迭代器。
每次请求一个值,就会执行生成器中的代码,直到遇到一个yield或者return语句。
yield语句意味着应该生成一个值
return语句意味着生成器要停止执行。
生成器由两部分组成:生成器的函数和生成器的迭代器
生成器的函数是用def语句定义的,包含yield的部分
生成器的迭代器是这个函数返回的部分
>>> def simple_generator():
yield 1
>>> simple_generator
<function simple_generator at 0x01405ED0>
>>> simple_generator()
<generator object simple_generator at 0x0140F7D8>
生成器的方法
外部作用域访问生成器的send方法
内部则挂器生成器,yield作为表达式而不是语句使用。
>>> def repeater(value):
while True:
new = (yield value)
if new is not None:
value = new
>>> r=repeater(42)
>>> r.__next__()
42
>>> r.send("Hello, world! ")
'Hello, world! '