Python進(jìn)階話題雜談(三)實(shí)用的特殊方法重載

1??普通運(yùn)算符的重載

運(yùn)算符重載并不是一個(gè)很簡單的話題启搂,但大多數(shù)的特殊情況并不實(shí)用窖式,本文只討論較常用的運(yùn)算符重載。首先痊项,Python中的各種數(shù)學(xué)運(yùn)算锅风,包括加減乘除等,都對應(yīng)著某個(gè)方法調(diào)用鞍泉。如1 + 2實(shí)際上對應(yīng)著形如1.__add__(2)的調(diào)用皱埠。所以,在程序中咖驮,用戶也可以修改這些函數(shù)边器,從而自定義這些運(yùn)算符號的行為。下面以__add__與__radd__為例托修,討論這樣的操作忘巧。

當(dāng)一個(gè)類重寫了__add__方法后,就可以對類實(shí)例使用加號運(yùn)算诀黍,__add__方法應(yīng)返回一個(gè)運(yùn)算后的結(jié)果:

class Test:

??? def __add__(self, other):

??????? return 1 + other

print(Test() + 10)

__add__方法有兩個(gè)形參,分別對應(yīng)于加號前后的兩個(gè)值仗处,所以上例中的__add__只是簡單的把加號后值加1的結(jié)果返回眯勾,輸出為11。

__radd__方法不太常用婆誓,這里僅作簡要說明吃环。當(dāng)執(zhí)行加法運(yùn)算時(shí),實(shí)際上是以self.__add__(other)的形式調(diào)用了加號左值的__add__方法洋幻,而如果由于加號兩邊對象不一致(如numpy中的ndarray可以與Python list進(jìn)行運(yùn)算)郁轻,加號左值并沒有定義__add__方法時(shí),此時(shí)就會顛倒加號的左右值,并嘗試調(diào)用加號右值的__radd__方法好唯。例如上文中的Test() + 10竭沫,如果換成10 + Test(),則調(diào)用將失敗骑篙,因?yàn)閕nt類型不能以Test()作為第二參數(shù)進(jìn)行__add__方法調(diào)用蜕提。此時(shí),如果Test類還定義了__radd__方法:

class Test:

??? def __add__(self, other):

??????? return 1 + other

??? def __radd__(self, other):

??????? return 1 + other

則此時(shí)如果進(jìn)行10 + Test()靶端,由于10.__add__(Test())將調(diào)用失敗谎势,那么就會嘗試Test().__radd__(10),顛倒左右參數(shù)杨名,并調(diào)用加號右值的__radd__方法脏榆。故大部分情況下,__add__和__radd__方法的定義都是類似的台谍。在實(shí)際情況中须喂,如果考慮到可能存在混合類型運(yùn)算時(shí),則可能需要考慮再定義__radd__方法典唇。

2??增強(qiáng)賦值運(yùn)算符的重載

增強(qiáng)賦值運(yùn)算在很多編程語言中都有體現(xiàn)镊折,其是一類由運(yùn)算符加上等號組成的自身運(yùn)算符。此類運(yùn)算符同時(shí)執(zhí)行運(yùn)算與賦值操作介衔,即對self本身進(jìn)行運(yùn)算與賦值恨胚。增強(qiáng)賦值運(yùn)算符與普通運(yùn)算符在重載時(shí)唯一的區(qū)別在于:普通運(yùn)算符返回的是運(yùn)算結(jié)果,而增強(qiáng)賦值運(yùn)算符返回的一定是self炎咖,這個(gè)self的值已經(jīng)通過運(yùn)算而改變赃泡,返回的self將通過賦值覆蓋掉原先的self。下面以__iadd__方法為例討論增強(qiáng)賦值運(yùn)算符的重載:

class Test:

??? def __init__(self, num):

??????? self.num = num

??? def __add__(self, value):

??????? return self.num + value

??? def __iadd__(self, value):

??????? self.num += value

??????? return self

testObj = Test(2)

testObj += 3

print(testObj.num)

上例中乘盼,__iadd__直接對self進(jìn)行運(yùn)算操作升熊,改變了self中的某些值,然后返回self本身绸栅。所以返回的這個(gè)self在增強(qiáng)賦值運(yùn)算之后會直接覆蓋掉運(yùn)算符左值级野,從而實(shí)現(xiàn)自身值的改變。而對比__add__方法粹胯,其僅僅返回了一個(gè)加法運(yùn)算的結(jié)果蓖柔,并不改變self本身。

3??__repr____str__的重載

__repr__與__str__分別對應(yīng)著Python中的repr與str函數(shù)調(diào)用风纠。關(guān)于這兩個(gè)函數(shù)的區(qū)別况鸣,一般上認(rèn)為:repr是生成“適合解釋器閱讀的格式”,而str則生成“適合用戶閱讀的格式”竹观,這樣的解釋很模糊镐捧,且在實(shí)際使用中并不需要特別關(guān)注這兩個(gè)函數(shù)的功能潜索。因?yàn)榻^大多數(shù)情況下,用戶一般都只會調(diào)用str函數(shù)而非repr函數(shù)懂酱,且print函數(shù)的輸出樣式也與str函數(shù)的返回值一致竹习,故以下主要針對__str__方法進(jìn)行討論。

在用戶自定義的類型中玩焰,__str__方法一般只與print函數(shù)連用由驹,起到明確、美化輸出的作用昔园。默認(rèn)情況下蔓榄,如果不定義__str__方法,則輸出某個(gè)類實(shí)例時(shí)會顯示“”這樣的字符串默刚,這就是print函數(shù)對當(dāng)前對象調(diào)用__str__方法的結(jié)果甥郑。而如果需要美化輸出,就可以重寫__str__方法荤西,返回一個(gè)期望的字符串:

class Test:

??? def __init__(self, num):

??????? self.num = num

??? def __str__(self):

??????? return '[%d]' % self.num

print(Test(2))

上例中澜搅,通過重載__str__方法,使得print(Test(2))時(shí)邪锌,不再輸出“”這樣的字符串勉躺,而是輸出了自定義的“[2]”字符串。

由于一般情況下均不關(guān)心__str__與__repr__的區(qū)別觅丰,故在重載__str__方法后饵溅,往往只需要將__repr__方法也綁定到__str__方法上即可:

__repr__ = __str__

同理,重載__repr__方法妇萄,然后__str__ = __repr__蜕企,也是可以的。

4??__call__的重載

__call__方法的重載應(yīng)用場合較少冠句,本文只做簡要討論轻掩。

首先,Python中針對一個(gè)對象懦底,可以使用多種“符號后綴語法”唇牧,這些語法背后都分別對應(yīng)著一個(gè)或一系列的方法調(diào)用。如一個(gè)對象后接“[...]”聚唐,則對應(yīng)著__*item__方法系列丐重;后接“.”,則對應(yīng)著__*attr__與__getattribute__方法系列拱层;而如果一個(gè)對象后接“(...)”弥臼,則對應(yīng)著代表函數(shù)調(diào)用的__call__方法宴咧。

當(dāng)重載一個(gè)類的__call__方法后根灯,這個(gè)類的對象就可以當(dāng)做一個(gè)函數(shù)一樣被調(diào)用,也可為__call__方法定義形參,則實(shí)際調(diào)用中就可以傳入相對應(yīng)的實(shí)參:

class Test:

??? def __init__(self, num):

??????? self.num = num

??? def __call__(self, value):

??????? print(self.num, value)

Test(2)('call')

當(dāng)__call__被定義后烙肺,Test(2)作為一個(gè)類實(shí)例纳猪,就可以當(dāng)做函數(shù)一樣調(diào)用,并可傳入相應(yīng)的參數(shù)桃笙。上述代碼的調(diào)用結(jié)果即為輸出“2 call”氏堤。


2018年6月于蘇州

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搏明,隨后出現(xiàn)的幾起案子鼠锈,更是在濱河造成了極大的恐慌,老刑警劉巖星著,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件购笆,死亡現(xiàn)場離奇詭異,居然都是意外死亡虚循,警方通過查閱死者的電腦和手機(jī)同欠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來横缔,“玉大人铺遂,你說我怎么就攤上這事【ジ眨” “怎么了襟锐?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斗蒋。 經(jīng)常有香客問我捌斧,道長,這世上最難降的妖魔是什么泉沾? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任捞蚂,我火速辦了婚禮,結(jié)果婚禮上跷究,老公的妹妹穿的比我還像新娘姓迅。我一直安慰自己,他們只是感情好俊马,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布丁存。 她就那樣靜靜地躺著,像睡著了一般柴我。 火紅的嫁衣襯著肌膚如雪解寝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天艘儒,我揣著相機(jī)與錄音聋伦,去河邊找鬼夫偶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛觉增,可吹牛的內(nèi)容都是我干的兵拢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼逾礁,長吁一口氣:“原來是場噩夢啊……” “哼说铃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘹履,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤腻扇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后砾嫉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衙解,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年焰枢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚓峦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡济锄,死狀恐怖暑椰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荐绝,我是刑警寧澤一汽,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站低滩,受9級特大地震影響召夹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恕沫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一第步、第九天 我趴在偏房一處隱蔽的房頂上張望所计。 院中可真熱鬧洽洁,春花似錦佃声、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叙身,卻和暖如春渔扎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背信轿。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工晃痴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妓忍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓愧旦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親定罢。 傳聞我的和親對象是個(gè)殘疾皇子笤虫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容