代碼重構(gòu)處理的不好,將會(huì)是意見非常令人頭疼的事情,因?yàn)橛锌赡軤可娴皆S多的團(tuán)隊(duì)和大量的代碼庫修改。那么如何高效、快速地進(jìn)行代碼重構(gòu),就是Python程序員值得掌握的技巧。本分介紹的是通過Python中的@property裝飾器,快速進(jìn)行代碼重構(gòu)的一個(gè)例子,供大家參考。從前,Python程序員Alice要打算創(chuàng)建一個(gè)代表金錢的類。她的第一個(gè)實(shí)現(xiàn)形式大概是下面這樣:
# 以美元為基礎(chǔ)貨幣的Money類的首個(gè)版本
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
# 還有其他一些方法,我們暫時(shí)不必理會(huì)
這個(gè)類后來被打包到一個(gè)Python庫里,并且慢慢地被許多不同的應(yīng)用使用。舉個(gè)例子,另一個(gè)團(tuán)隊(duì)中的Python程序員Bob是這樣使用Money類的:
money = Money(27, 12)
message = "I have {:d} dollars and {:d} cents."
print(message.format(money.dollars, money.cents))
# "I have 27 dollars and 12 cents."
money.dollars += 2
money.cents += 20
print(message.format(money.dollars, money.cents))
# "I have 29 dollars and 32 cents."
這樣使用并沒有錯(cuò),但是卻出現(xiàn)了代碼可維護(hù)性的問題。你發(fā)現(xiàn)了嗎?
幾個(gè)月或是幾年之后。Alice想要重構(gòu)Money類的內(nèi)部實(shí)現(xiàn),不再記錄美元和美分,而是僅僅記錄美分,因?yàn)檫@樣做可以讓某些操作簡(jiǎn)單很多。下面是她很可能會(huì)作的修改:
# Money類的第二個(gè)版本
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
這一修改帶來一個(gè)后果:引用Money類的每一行代碼都必須要調(diào)整。有時(shí)候很幸運(yùn),你就是所有這些代碼的維護(hù)者,只需要自己直接重構(gòu)即可。但是Alice的情況就沒有這么好了;許多團(tuán)隊(duì)都復(fù)用了她的代碼。因此,她需要協(xié)調(diào)他們的代碼庫與自己的修改保持一致,也許甚至要經(jīng)歷一段特別痛苦、漫長(zhǎng)的正式棄用過程(deprecation process)。
幸運(yùn)的是,Alice知道一種更好的解決辦法,可以避免這個(gè)令人頭疼的局面出現(xiàn):使用Python內(nèi)建的property裝飾器。@property一般應(yīng)用在Python方法上,可以有效地將屬性訪問(attribute access)變成方法調(diào)用(method call)。舉個(gè)例子,暫時(shí)將Money類拋至一邊,假設(shè)有一個(gè)代表人類的Person類(class):
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
@property
def full_name(self):
return '{} {}'.format(self.first, self.last)
代碼樣式不同,是因?yàn)橹坝玫墓ぞ叱鰡栴}了?!狤arlGrey
請(qǐng)注意full_name方法。除了在def語句上方裝飾了@property之外,該方法的聲明沒有什么不同的地方。但是,這卻改變了Person對(duì)象的運(yùn)作方式:
>>> buddy = Person('Jonathan', 'Doe')
>>> buddy.full_name
'Jonathan Doe'
我們發(fā)現(xiàn),盡管full_name被定義為一個(gè)方法,但卻可以通過變量屬性的方式訪問。在最后一行代碼中沒有()操作符;我并沒有調(diào)用full_name方法。我們所做的,可以說是創(chuàng)建了某種動(dòng)態(tài)屬性。
回到本文中的Money類,Alice對(duì)它作了如下修改:
# Money類的最終版本
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
@property
def dollars(self):
return self.total_cents // 100;
@dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
@property
def cents(self):
return self.total_cents % 100;
@cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
除了使用@property裝飾器定義了dollars屬性的getter外,Alice還利用@dollars.setter創(chuàng)建了一個(gè)setter。Alice還對(duì)cents`屬性作了類似處理。
那么現(xiàn)在,Bob的代碼要做哪些相應(yīng)的修改呢?根本不用改!
# 他的代碼完全沒有變動(dòng),但是卻可以正常調(diào)用Money類。
money = Money(27, 12)
message = "I have {:d} dollars and {:d} cents."
print(message.format(money.dollars, money.cents))
# "I have 27 dollars and 12 cents."
money.dollars += 2
money.cents += 20
print(message.format(money.dollars, money.cents))
# "I have 29 dollars and 32 cents."# 代碼邏輯也沒有問題。
money.cents += 112
print(message.format(money.dollars, money.cents))
# "I have 30 dollars and 44 cents."
事實(shí)上,所有使用了Money類的代碼都不需要進(jìn)行修改。Bob不知道或根本不在乎Alice去除了類中的dollars和cents屬性:他的代碼還是和以前一樣正常執(zhí)行。唯一修改過的代碼就是Money類本身。
正是由于Python中處理裝飾器的方式,你可以在類中自由使用簡(jiǎn)單的屬性。如果你所寫的類改變了管理狀態(tài)的方法,你可以自信地通過@property裝飾器對(duì)這個(gè)類(且只有這個(gè)類)進(jìn)行修改。這是一個(gè)共贏的方法!相反,在Java等語言中,程序員必須主動(dòng)去定義訪問屬性的方法(例如getDollars或setCents)。
最后要提示大家:
這種方法對(duì)于那些被其他程序員和團(tuán)隊(duì)復(fù)用的代碼最為重要。假設(shè)僅僅是在你自己一個(gè)維護(hù)的應(yīng)用中創(chuàng)建一個(gè)類似Money的類,那么如果你改變了Money的接口,你只需要重構(gòu)自己的代碼就可以。這種情況下,你沒有必要像上面說的那樣使用@property裝飾器。
原文:http://migrateup.com/python-properties-refactoring/
譯文:http://codingpy.com/article/python-properties-refactoring/
你是怎樣重構(gòu)Python代碼的?
歡迎留言和我們分享
印度小伙寫了套深度學(xué)習(xí)教程,Github上星標(biāo)已經(jīng)5000+上百個(gè)數(shù)據(jù)文件合并,只能手動(dòng)復(fù)制粘貼?教你一招十秒搞定!一個(gè)提升圖像識(shí)別準(zhǔn)確率的精妙技巧一文讀懂:從 Python 打包到 CLI 工具如何使用 Python 進(jìn)行時(shí)間序列預(yù)測(cè)?美亞Kindle排名第一的Python 3入門書,火遍了整個(gè)編程圈十分鐘搭建私有 Jupyter Notebook 服務(wù)器使用 Python 制作屬于自己的 PDF 電子書
12步輕松搞定Python裝飾器200 行代碼實(shí)現(xiàn) 2048 游戲