標籤:
準備工作
為了確保類是新型類,應該把 _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參數會被自動綁定到執行個體上(這稱為Binder 方法)。但如果直接調用類的方法,那麼就沒有執行個體會被綁定。這樣就可以自由地提供需要的self參數(這樣的方法稱為未Binder 方法)。
通過將當前的執行個體作為self參數提供給未Binder 方法,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 方法(調用時不用參數)用於停止產生器。
python學習筆記(方法、屬性、迭代器)