一招快速重構 Python 代碼

代碼重構處理的不好,將會是意見非常令人頭疼的事情,因為有可能牽涉到許多的團隊和大量的代碼庫修改。那麼如何高效、快速地進行代碼重構,就是Python程序員值得掌握的技巧。

本分介紹的是通過Python中的@property裝飾器,快速進行代碼重構的一個例子,供大家參考。

從前,Python程序員Alice要打算創建一個代表金錢的類。她的第一個實現形式大概是下面這樣:

# 以美元為基礎貨幣的Money類的首個版本

class Money:

def __init__(self, dollars, cents):

self.dollars = dollars

self.cents = cents

# 還有其他一些方法,我們暫時不必理會

這個類後來被打包到一個Python庫裡,並且慢慢地被許多不同的應用使用。舉個例子,另一個團隊中的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."

這樣使用並沒有錯,但是卻出現了代碼可維護性的問題。你發現了嗎?

幾個月或是幾年之後。Alice想要重構Money類的內部實現,不再記錄美元和美分,而是僅僅記錄美分,因為這樣做可以讓某些操作簡單很多。下面是她很可能會作的修改:

# Money類的第二個版本

class Money:

def __init__(self, dollars, cents):

self.total_cents = dollars * 100 + cents

這一修改帶來一個後果:引用Money類的每一行代碼都必須要調整。有時候很幸運,你就是所有這些代碼的維護者,只需要自己直接重構即可。但是Alice的情況就沒有這麼好了;許多團隊都複用了她的代碼。因此,她需要協調他們的代碼庫與自己的修改保持一致,也許甚至要經歷一段特別痛苦、漫長的正式棄用過程(deprecation process)。

幸運的是,Alice知道一種更好的解決辦法,可以避免這個令人頭疼的局面出現:使用Python內建的property裝飾器。@property一般應用在Python方法上,可以有效地將屬性訪問(attribute access)變成方法調用(method call)。舉個例子,暫時將Money類拋至一邊,假設有一個代表人類的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)

代碼樣式不同,是因為之前用的工具出問題了。—EarlGrey

請注意full_name方法。除了在def語句上方裝飾了@property之外,該方法的聲明沒有什麼不同的地方。但是,這卻改變了Person對象的運作方式:

>>> buddy = Person('Jonathan', 'Doe')

>>> buddy.full_name

'Jonathan Doe'

我們發現,儘管full_name被定義為一個方法,但卻可以通過變量屬性的方式訪問。在最後一行代碼中沒有操作符;我並沒有調用full_name方法。我們所做的,可以說是創建了某種動態屬性。

回到本文中的Money類,Alice對它作了如下修改:

 

# 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創建了一個setter。Alice還對cents`屬性作了類似處理。

那麼現在,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."# 代碼邏輯也沒有問題。

money.cents += 112

print(message.format(money.dollars, money.cents))

# "I have 30 dollars and 44 cents."

事實上,所有使用了Money類的代碼都不需要進行修改。Bob不知道或根本不在乎Alice去除了類中的dollars和cents屬性:他的代碼還是和以前一樣正常執行。唯一修改過的代碼就是Money類本身。

正是由於Python中處理裝飾器的方式,你可以在類中自由使用簡單的屬性。如果你所寫的類改變了管理狀態的方法,你可以自信地通過@property裝飾器對這個類(且只有這個類)進行修改。這是一個共贏的方法!相反,在Java等語言中,程序員必須主動去定義訪問屬性的方法(例如getDollars或setCents)。

最後要提示大家:

這種方法對於那些被其他程序員和團隊複用的代碼最為重要。假設僅僅是在你自己一個維護的應用中創建一個類似Money的類,那麼如果你改變了Money的接口,你只需要重構自己的代碼就可以。這種情況下,你沒有必要像上面說的那樣使用@property裝飾器。

原文:http://migrateup.com/python-properties-refactoring/

譯文:http://codingpy.com/article/python-properties-refactoring/

你是怎樣重構Python代碼的?

如果覺得文章對你有所幫助,歡迎點贊並且推薦給你的好友。

CODE優選·好物

【谷歌騰訊都在用 馬雲俞敏洪推薦】

米喬減壓腰墊二代升級——氣動版

掃描上方二維碼瞭解詳情

一招快速重构 Python 代码

印度小夥寫了套深度學習教程,Github上星標已經5000+

上百個數據文件合併,只能手動複製粘貼?教你一招十秒搞定!

一個提升圖像識別準確率的精妙技巧

一文讀懂:從 Python 打包到 CLI 工具

如何使用 Python 進行時間序列預測?

美亞Kindle排名第一的Python 3入門書,火遍了整個編程圈

十分鐘搭建私有 Jupyter Notebook 服務器

使用 Python 製作屬於自己的 PDF 電子書

12步輕鬆搞定Python裝飾器

200 行代碼實現 2048 遊戲


分享到:


相關文章: