python学习笔记(方法、属性、迭代器)

准备工作

为了确保类是新型类,应该把 _metaclass_=type 入到你的模块的最开始

class NewStyle(object):
more_code_here
class OldStyle:
more_code_here

在这个两个类中NewType是新类,OldType是属于旧类,如果前面加上 _metaclass_=type ,那么两个类都属于新类。

构造方法

 构造方法与其的方法不一样,当一个对象被创建会立即调用构造方法。创建一个python的构造方法很简答,只要把init方法,从简单的init方法,转换成魔法版本的_init_方法就可以了。

class FooBar:

def __init__(self):

self.somevar = 42

>>> f =FooBar()

>>> f.somevar

42

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

每一个类都可能拥有一个或多个超类(父类),它们从超类那里继承行为方法。

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方法,B类继承了A类,所以会调用A 类的hello方法。 

在子类中增加功能功能的最基本的方式就是增加方法。但是也可以重写一些超类的方法来自定义继承的行为。如下:

class A:
def hello(self):
print "Hello,I'm A."
class B(A):
def hello(self):
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!
>>>

这个类中定义了鸟有吃的能力, 当它吃过一次后再次就会不饿了,通过上面的执行结果可以清晰的看到。

那么用SongBird类来继承Bird 类,并且给它添加歌唱的方法:

class Bird:
def __init__(self):
self.hungry=True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry=False
else:
print 'No,thanks!'

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

-----------

>>> sb=SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()

Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
sb.eat()
File "F:/python/myDemo/_init_.py", line 5, in eat
if self.hungry:
AttributeError: SongBird instance has no attribute 'hungry'
>>>

异常很清楚地说明了错误:SongBird没有hungry特性。原因是这样的:在SongBird中,构造方法被重写,但新的构造方法没有任何关于初始化hungry特性的代码。为了达到预期的效果,SongBird的构造方法必须调用其超类Bird的构造方法来确保进行基本的初始化。

两种方法实现:

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

class Bird:
def __init__(self):
self.hungry=True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry=False
else:
print 'No,thanks!'

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!
>>>

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

通过将当前的实例作为self参数提供给未绑定方法,SongBird就能够使用其超类构造方法的所有实现,也就是说属性hungry能被设置。

使用super函数

__metaclass__=type
class Bird:
def __init__(self):
self.hungry=True
def eat(self):
if self.hungry:
print 'Aaaah...'
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

----------

>>> sb=SongBird()
>>> sb.sing()
Squawk!
>>> sb.eat()
Aaaah...
>>> sb.eat()
No,thanks!
>>>

基本的映射和序列规则

序列和映射是对象的集合。

__len(self)__:

返回集合中所含项目的数量。对序列来说,这就是元素的个数,对映射来说,这就是键~值对的数量

__getitem__(self,key):

返回与所给键对应的值

__setitem__(self,key,value):

按一定方法存储与key相关的value

__delitem__(self,key):

对一部分对象使用del语句时被调用,同时必须删除和元素相关的键

def checkIndex(key):
'''
所给的键是能接受的索引吗?
为了能被接受,键应该是一个非负的整数,如果它不是一个整数,会引发TypeError,如果它是负数,
则会引发IndexError(因为序列是无限长的)
'''
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
>>> s[6]
13
>>> s['four']

Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
s['four']
File "F:/python/myDemo/_init_.py", line 21, in __getitem__
checkIndex(key)
File "F:/python/myDemo/_init_.py", line 8, in checkIndex
raise TypeError
TypeError
>>> s[-42]

Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
s[-42]
File "F:/python/myDemo/_init_.py", line 21, in __getitem__
checkIndex(key)
File "F:/python/myDemo/_init_.py", line 10, in checkIndex
raise IndexError
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)

-------------------------

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

属性

访问器是一个简单的方法,它能够使用getHeight 、setHeight 之样的名字来得到或者重绑定一些特性。如果在访问给定的特性时必须要采取一些行动,那么像这样的封装状态变量就很重要。如下:

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((150,100))

>>> r.width
150

在上面的例子中,getSize和setSize方法一个名为size的假想特性的访问器方法,size是由width 和height构成的元组。

property函数

__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=Rectangle()
>>> r.width=10
>>> r.height=5
>>> r.size
(10, 5)
>>> r.size=150,100
>>> r.width
150
>>>

在这个新版的Retangle 中,property 函数创建了一个属性,其中访问器函数被用作参数(先取值,然后是赋值),这个属性命为size 。这样一来就不再需要担心是怎么实现的了,可以用同样的方式处理width、height 和size。

静态方法和类成员方法

静态方法定义没有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()
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>
>>>

__getattr__、__setattr__和它的朋友们

__getattr__(self,name):

当特性name被访问且对象没有相应的特性时被自动调用

__setattr__(self,name,value):

当试图给特性name赋值时被自动调用

__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
def __getattr__(self,name):
if name=='size':
return self.width,self.height
else:
raise AttributeError

 

迭代器

本节进行迭代器的讨论。只讨论一个特殊方法---- __iter__  ,这个方法是迭代器规则的基础

迭代器规则

迭代的意思是重复做一些事很多次---就像在循环中做的那样。__iter__ 方法返回一个迭代器,所谓迭代器就是具有next方法的对象,在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常。

 

这里是一个婓波那契数例,使用迭代器如下:

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


1597
>>>

从迭代器得到序列

 除了在迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下,能使用迭代器替换。

>>> 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]
>>>

生成器

创建生成器

def flatten(nested):
for sublist in nested:
for element in sublist:
yield element

>>> nested=[[1,2],[3,4],[5]]
>>> for num in flatten(nested):
print num


1
2
3
4
5
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
>>>

递归生成器

上面创建的生成器只能处理两层嵌套,为了处理嵌套使用了两个for循环,如果要处理任意层的嵌套呢?例如,可以每层嵌套需要增加一个for循环,但不知道有几层嵌套,所以必须把解决方案变得更灵活,现在可以用递归来解决

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]
>>>

当flatten被调用时有两种情况:基本情况和需要递归的情况

在基本的情况中,函数被告知展开一个元素,这种情部下,for循环会引发一个TypeError 异常,生成会产生一个元素。

如果展开的是一个列表,那么就需要特殊情况处理。程序必须遍历所有的子列表,并对它们调用flatten。

上面的做法有一个问题:如果aa 是一个类似于字符串的对象(字符串、Unicode、UserString等),那么它就是一个序列,不会引发TypeError,但是你不想对这样的对象进行迭代。

为了处理这种情况,则必须在生成器的开始处添加一个检查语句。试着将传入的对象和一个字符串拼接,看看会不会出现TypeError,这是检查一个对象是不是类似于字符串最简单快速的方法。

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']
>>>

如果nested+’’ 引发了一个TypError ,它就会被忽略。如果没有引发TypeError,那么内层try语句就会引发一个它自己的TypeError异常。

生成器方法

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!'
>>>

生成器的另两个方法:

throw方法(使用异常类型调用,还有可选的值以及回溯对象)用于在生成器内引发一个异常(在yield表达式中)

close 方法(调用时不用参数)用于停止生成器。

 

posted @ 2015-08-11 16:04  whats  阅读(331)  评论(0编辑  收藏  举报