筆記更新于2019年11月28日盟迟,
摘要:常用定方法:__str__( )压鉴;__repr__( )惧所;__iter__( )與__next__( )骤坐;__getitem__( )绪杏;__setitem__( )下愈;__delitem__( );__getattr__( )蕾久;__call__( )
寫在前面:為了更好的學習python势似,博主記錄下自己的學習路程。本學習筆記基于廖雪峰的Python教程僧著,如有侵權履因,請告知刪除。歡迎與博主一起學習Pythonヽ( ̄▽ ̄)?
文章目錄
面向對象編程
__str__( )
__repr__( )
__iter__( )與__next__( )
__getitem__( )
__setitem__( )
__delitem__( )
__getattr__( )
__call__( )
面向對象編程
之前已經介紹過形似__xxx__的是特殊變量或函數盹愚,如__init__栅迄、__slots__等。這一節(jié)將介紹更多的特殊方法皆怕,來幫助我們定制自己定義到class毅舆。
__str__( )
__str__( )方法可以根據定義的字符串,在打印實例時返回所定義的字符串愈腾。
在沒有使用__str__( )方法時:
>>>class Stu(object): #定義一個class
... def __init__(self, name):
... self.name = name
...
>>>print(Stu('Ming')) #打印一個實例
<__main__.Stu object at 0x00000000020E8550>
使用了__str__( )方法后:
>>>class Stu(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Stu object (name: %s)' % self.name
...
>>>print(Stu('MIng'))
Stu object (name: MIng)
這樣可以清楚地看到實例內部的重要數據憋活,需要注意的是,__str__( )方法里的return后面跟的必須是字符串虱黄。
__repr__( )
__repr__( )的用途與__str__( )很像悦即,兩者的區(qū)別是__str__( )返回的是用戶看到的字符串,而__repr__( )返回的是程序開發(fā)者看到的字符串。
也就是說辜梳,如按上面的定義粱甫,在交互模式中直接調用實例,還是不會返回想要的字符串的作瞄。
>>>Stu('Ming')
<__main__.Stu object at 0x00000000020E8550>
我們把上面的__str__( )改為__repr__( )魔种,再試試。
>>>class Stu(object):
... def __init__(self, name):
... self.name = name
... def __repr__(self):
... return 'Stu object (name: %s)' % self.name
...
>>>Stu('MIng')
Stu object (name: MIng)
成功返回粉洼。此時如果使用print节预,還是能夠返回想要的字符串的。
>>>print(Stu('Ming'))
Stu object (name: MIng)
如果同時定義__str__( )和__repr__( )呢属韧?
class Stu(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Stu object (name: %s)' % self.name
def __str__(self):
return 'str'
>>>Stu('MIng')
Stu object (name: MIng)
>>>print(Stu('Ming'))
str
可見在這種情況下安拟,print是優(yōu)先考慮__str__( )的。
總而言之宵喂,__str__( )面向用戶糠赦,而__repr__( )面向程序員。所以如果想要在所有環(huán)境下統(tǒng)一顯示的話锅棕,直接用__repr__即可拙泽。若要區(qū)分,則定義兩個裸燎。一般情況下顯示的內容時相同的顾瞻,可用__repr__( ) = __str__( )來簡化定義。
__iter__( )與__next__( )
for...in循環(huán)是作用于可迭代對象德绿,那想要讓自己定義的類可用for...in循環(huán)荷荤,就必須把自己變成可迭代對象。
__iter__( )方法的用途就是返回一個可迭代對象Iterable移稳。
__next__( )方法的用途是定義一個循環(huán)的邏輯而返回下一個循環(huán)值(用了之后就是一個迭代器對象了Iterator)蕴纳。
那么結合兩種方法,for..in循環(huán)就會不斷調用該可迭代對象作用于__next__( )方法得出下一個循環(huán)值个粱。直到遇到StopIteration錯誤時退出循環(huán)古毛。
舉個例子,如生成斐波那契數列都许,寫個Fib類稻薇。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
def __iter__(self):
return self # 實例本身就是迭代對象梭稚,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a > 1000: # 退出循環(huán)的條件
raise StopIteration()
return self.a # 返回下一個值
用for..in作用于Fib的實例
>>>for n in Fib():
... print(n)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
實際上颖低,不用__iter__( )方法也可以得到斐波那契數列,只不過需要不斷調用next( )來拿取下一個值弧烤,而用了__iter__( )方法之后變成可迭代對象夫啊,就可以用for...in循環(huán)來輕松拿取了。
__getitem__( )
__getitem__( )方法的用途是讓實例像list那樣按照下標取元素纬纪。__getitem__( )方法需要傳入兩個參數催跪,一個是實例本身,另一個是下標。當檢測到有[ ]時就會運行__getitem__( )。
>>>class A(object):
... def __getitem__(self, key): # 定義一個__getitem__方法,key參數是下標
... print('Call function __getitem__')
... print(key + 1)
>>>a = A()
>>>a[1] # 在這里會把a傳給self瘪校,而把1傳給key
Call function __getitem__ # 當檢測到有[]時,就會執(zhí)行__getitem__方法
2 # 返回的是key+1名段,即1+1=2
利用__getitem__( )方法的特性阱扬,我們可以生成一個能夠按下標輸出的斐波那契數列。
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
試一下:
>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
若還想要想list那樣具有切片功能伸辟,就需要判斷傳入的參數是整數還是切片麻惶,然后進一步做輸出處理。像這樣(以下代碼轉自廖雪峰的官方網站)
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
運行結果:
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
當然還有地方需要改進信夫,如對step參數的處理窃蹋,對負數的處理等等,這些都可以在后面繼續(xù)完善静稻,這里只簡單介紹__getitem__( )的用法警没。
__setitem__( )
__setitem__( )同樣可以讓實例像list那樣按照下標取元素,同時還可以給key賦值value振湾,所以需要傳入三個參數杀迹,分別是實例本身,鍵key恰梢,值value佛南。當檢測到有形如[ ] = 的賦值語句時就會執(zhí)行__setitem__( )方法。
>>>class A(object):
... def __setitem__(self, key, value): # 定義一個__setitem__方法嵌言,key參數是鍵,value是值
... print('Call function __setitem__')
... print(value)
>>>a = A()
>>>a[1] = 10 # 在這里會把a傳給self及穗,而把1傳給key摧茴,把10傳給value
Call function __getitem__ # 當檢測到有[] = 時,就會執(zhí)行__setitem__方法
10 # 打印value
__delitem__( )
__delitem__( )用來刪除指定key的元素埂陆,需要傳入self和key兩個參數苛白。當檢測到del時就會執(zhí)行__delitem__( )方法。
結合__getitem__( )焚虱、__setitem__( )和__delitem__( ),方法购裙,給個簡單的例子再幫助理解。
class A(object):
def __init__(self, start = 0):
print('Call function __init__')
self.start = start
self.dict = {} # 定義一個dict
def __getitem__(self, key): # 定義獲取值的方法
print('Call function __getitem__')
try:
return self.dict[key] # 如果有對key賦值鹃栽,則返回key對應的value
except KeyError:
return key # 如果沒有對key賦值躏率,則返回key本身
def __setitem__(self, key, value): # 定義賦值方法
print('Call function __setitem__')
self.dict[key] = value
def __delitem__(self, key): # 定義刪除元素的方法
print('Call function __delitem__')
del self.dict[key]
a = A() # 創(chuàng)建A的一個實例a
Call function __init__
a[1] = 10 # 執(zhí)行賦值方法__setitem__
Call function __setitem__
a[2] = 20 # 執(zhí)行賦值方法__setitem__
Call function __setitem__
a[1] # 執(zhí)行取值方法__getitem__,[1]有對應的值10
Call function __getitem__
10
a.dict #dict屬性中已有的值
{1: 10, 2: 20}
del a[1] #刪除dict屬性中,key為[1]的值
Call function __delitem__
a.dict
{2: 20}
__getattr__( )
當我們嘗試調用一個沒有被定義的屬性或方法時薇芝,會出現錯誤蓬抄。而__getattr__( )的用途是當調用不存在的屬性時,就會執(zhí)行__getattr__( )嘗試返回另外的值夯到。
未定義__getattr__( )時
class Stu(object):
def __init__(self, name):
self.name = name
a = Stu('Ming')
>>>a.name
Ming
>>>a.age
AttributeError: 'Stu' object has no attribute 'age'
定義了__getattr__( )之后
class Stu(object):
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
return 1
>>>a.name
Ming
>>>a.age # 這里的age將會傳給__getattr__()的第二個參數attr
1
也能返回一個函數
class Stu(object):
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
return lambda: 10
當然嚷缭,我們能根據__getattr__( )的參數,去完善定義耍贾,像這樣:
class Stu(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 10
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
__call__( )
__call__( )方法可以使實例變?yōu)榭烧{用對象阅爽,當嘗試直接調用實例時,會執(zhí)行__call__( )方法荐开。
class Stu(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('I am %s' % self.name)
>>>a = Stu('Ming')
>>>a() # 注意需要加()
I am Ming
__call__( )還能定義參數优床,這樣的話就可以實例對象看成一個函數了。這就模糊了對象和函數的區(qū)別誓焦,其實很多時候我們需要區(qū)分的是對象是否可調用胆敞,可以用callable( )函數來判斷。
>>> callable(Stu())
True
>>> callable(max)
True
>>> callable([1, 2])
False
>>> callable(None)
False
以上就是本節(jié)的全部內容杂伟,感謝你的閱讀移层。
下一節(jié)內容:16.面向對象編程之 裝飾器@preperty和枚舉類的介紹
有任何問題與想法,歡迎評論與吐槽赫粥。
和博主一起學習Python吧( ̄▽ ̄)~*