一個 Pythonic 的類應不應該在 __init__ 中檢查參數有效性?

來源:互聯網
上載者:User
我知道按照鴨子類型原則,是不應該檢查參數類型的。但是應該檢查參數在其他方面的有效性嗎?例如:
class Comment(object):
... def __init__(self, content, creater, ipaddress):
... ... self.content = content
... ... self.creater = creater
... ... self.ipaddress = ipaddress
似乎大家一般都這麼寫,而不檢查 ipaddress 是否是有效 IP 位址。
Python 的編碼宗旨似乎是信任開發人員,所以 private 之類的設計都沒有。那麼我們的信任應該到“不檢查參數有效性”的程度嗎?

回複內容:

其實這個問題已經被討論過了,“我們都是成年人” (After all, we're all consenting adults here.)

簡單翻譯一段:
Nothing is really private in python. No class or class instance can
keep you away from all what's inside (this makes introspection
possible and powerful). Python trusts you. It says "hey, if you want
to go poking around in dark places, I'm gonna trust that you've got
a good reason and you're not making trouble."

After all, we're all consenting adults here.

Python中沒有東西是真正private的. 任何類和對象都不會阻止你窺探他們的內部,這使得Python的自省異常強大. Python 對你的態度是信任的,它說:“hey,如果你想嘗試使用黑魔法,那麼放心大膽的去吧,我相信你一定是有自己的理由的,知道自己在幹嘛,並且不會捅出簍子,懂得點到為止”

話說回來,大家畢竟都是成年人了,懂得為自己的行為負責,都知道自己在幹嘛,所以你想幹什麼我不會攔著你。

我想這種設計哲學才是 Python 與 Java 等語言的根本區別,對於參數檢查也是一樣的,不檢查你的參數,因為信任你。但是出了問怎麼樣呢?當然是拋異常。拋了異常,你就要自己負責(在合適的地方 try except,並且假設你知道捕獲異常後應該怎麼處理)。

再扯的遠一點,這種思想貫穿在 Python 中很多地方,比如想讓一個 Generator 結束,可以拋一個 StopIteration 異常,異常不是洪水猛獸,而是一種正常現象,異常的正確拋出和捕獲保證了世界正常運轉,在 Python 裡重要性堪比控制流程。為什麼呢?也是一種信任的思想,相信異常可以被正確處理,也應該被正確處理,並且已經在語言自身中處處都被正確處理著。

題主所寫的那個類,如果你傳了一個有問題的參數,那程式通常就會在應該出問題的地方拋異常。而這個類的作者——你作為一個成年人,對這個異常應該能正確處理,這是對你信任。你應該知道怎麼處理這個異常,包括你知道在有必要的時候應該做一定的參數檢查(這不衝突,依舊是信任。所以你看,你想做參數檢查Python也不攔著你)。

小時候什麼都不懂,我說想自己騎車去另一個城市玩,被我媽攔住了(參數檢查).
如果我現在說要去,我媽說:“你是成年人了,你應該有自己的理由吧,照顧好自己,但是你出行的一切問題由你自己負責”(信任,不限制,假設使用者能夠自己對問題負責)。

全文,見:[Tutor] What is "pythonic"? 輸入參數的檢查是防禦性編程的一部分

在你不信任輸入的時候,比如這是使用者提供的資料,或者另一台伺服器提供的,那應該就要檢查

但是未必要在類初始化時檢查,如果這個是 Model 類,也可以在寫入資料庫之前檢查

如果這個輸入,是由一個值得信任的模組提供的話,或者已經經過一輪檢查,就沒必要重複了
Python 的宗旨是信任開發人員會遵守開發約定,但不是說信任開發人員的程式沒有 Bug,和防禦性編程也沒有任何衝突在文檔裡標明需要什麼樣的資料就好了。Python 官方是不支援不必要地檢查類型的。既然連類型都不檢查了,為什麼要檢查值的有效性呢?C 語言也同樣不會總是去檢查使用者傳過來的東西是不是正確的——這是調用者的責任,庫的責任在於把文檔弄好,別讓人猜哪裡該傳入何種資料。

在 Python 中,不合法的資料傳入之後總是會導致異常發生的(否則你為什麼說它不是合法的?)。如果你馬上要把那個 IP 位址儲存到資料庫中,或者交給 C 函數使用,那麼肯定是要檢查的;如果是傳給 socket.connect 之類的,何必檢查呢?你又能否做到正確地檢查?(絕大部分網站註冊時的 Email 檢查過嚴,比如不允許 + 號;至於 IP 位址,有多少人考慮到了 IPv6?你們都知道秒的取值範圍是 0-61 嗎?)

個人的理解是這是一個與語言無關的問題,不僅僅是python。

將業務的處理和檢查傳入的參數做分離是一個不錯的選擇,但是這個分離是類層級,還是方法層級可能就需要根據具體的業務權衡後決定。可能需要實現的功能比較簡單,參數也比較簡單,方法級就可以了,如果後續需求變更了,商務邏輯複雜了,再重構代碼也不遲。

基於單一職責原則,一個類做好一件事。個人比較偏愛業務處理成一個獨立的類,參數檢查為另一個類,並將這兩個類放一個檔案。

江閣同學的疑惑是:是否應該檢查參數除類型外的有效性。
從安全、資料完整性等方面考慮,這種類型檢查是有必要的,如前面依雲同學提到的資料庫操作等。這些檢查應該是與具體業務相關的,不應該是在最後寫入前再檢查(寫入前也是要檢查的,不要相信輸入)。簡單的答案是:Python代碼不應該設計成需要類型檢查。如果你覺得需要,就說明設計有問題。
這篇stackoverflow的答案很好,建議閱讀:
http://stackoverflow.com/questions/734368/type-checking-of-arguments-python 事實上,你完全可以通過metaclass等機制構建一個強制類型檢查的類。輸入可能不規範就要檢查, 不過不一定在__init__裡, 可以調另外的函數, 比如
self.ipaddress = self.validateip(ipaddress)

其實不用太在乎什麼python規範, 很多東西沒什麼規範, 視事而為.


另外檢查ip地址推薦Google開源的python庫 http://code.google.com/p/ipaddr-py/
類不檢查,哪就在輸入的時候把把關就可以了。Duck typing 不需要檢查代碼類型的邏輯是:當類型不合適的時候自然會出錯。但這與防禦性編程並不矛盾。你仍然應該在模組邊界對輸入有效性進行判斷,不然等到長長的調用鏈末端(如果是傳入類建構函式儲存起來,調用鏈早就斷了)才發現不合適的時候,你怎麼調試?給自己以及項目組其他人這個地方的"默契" 是, 非商務邏輯代碼比如corelib, utils 對參數進行檢查, 商務邏輯部分使用者輸入檢查...其他的並不強制.
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.