【Python入門】15.面向對象編程之 定制類常用的定制方法

筆記更新于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吧( ̄▽ ̄)~*

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末观话,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子越平,更是在濱河造成了極大的恐慌频蛔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秦叛,死亡現場離奇詭異晦溪,居然都是意外死亡,警方通過查閱死者的電腦和手機挣跋,發(fā)現死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門三圆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人避咆,你說我怎么就攤上這事舟肉。” “怎么了查库?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵路媚,是天一觀的道長。 經常有香客問我樊销,道長整慎,這世上最難降的妖魔是什么脏款? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮院领,結果婚禮上弛矛,老公的妹妹穿的比我還像新娘。我一直安慰自己比然,他們只是感情好丈氓,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著强法,像睡著了一般万俗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饮怯,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天闰歪,我揣著相機與錄音,去河邊找鬼蓖墅。 笑死库倘,一個胖子當著我的面吹牛,可吹牛的內容都是我干的论矾。 我是一名探鬼主播教翩,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贪壳!你這毒婦竟也來了饱亿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤闰靴,失蹤者是張志新(化名)和其女友劉穎彪笼,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體蚂且,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡配猫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了膘掰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片章姓。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖识埋,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情零渐,我是刑警寧澤窒舟,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站诵盼,受9級特大地震影響惠豺,放射性物質發(fā)生泄漏银还。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一洁墙、第九天 我趴在偏房一處隱蔽的房頂上張望蛹疯。 院中可真熱鬧,春花似錦热监、人聲如沸捺弦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽列吼。三九已至,卻和暖如春苦始,著一層夾襖步出監(jiān)牢的瞬間寞钥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工陌选, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留理郑,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓咨油,卻偏偏與公主長得像您炉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子臼勉,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容