2011-05-02 32 views
2

一些Python方法適用於各種輸入源。例如,XML元素樹parse方法接受一個對象,該對象可以是字符串(在這種情況下API將其視爲文件名),也可以是支持IO接口的對象,如文件對象或io.StringIOPython 3:確定對象是否支持IO

因此,顯然parse方法正在做某種接口嗅探,以確定採取哪種行動。我想最簡單的方法是通過isinstance(x, str)來檢查輸入參數是否是字符串,如果是這樣,則將其視爲文件名,否則將其視爲IO對象。

但爲了更好的錯誤檢查,我認爲最好檢查x是否支持IO接口。什麼是標準的慣用方法來檢查一個對象是否支持指定的接口?

的一種方式,我想,是隻說:

if "read" in x.__class__.__dict__: # check if object has a read method

但是,僅僅因爲x有一個「讀」的方法並不一定意味着它支持IO接口,所以我想我還應檢查IO接口中的每種方法。這通常是做這件事的最好方法嗎?或者我應該忘記檢查界面,並讓一個可能的AttributeError進一步處理堆棧?

回答

3

Python強烈鼓勵鴨子打字:假設傳入的對象是有效的並嘗試使用它。這樣,您的代碼儘可能靈活。當然,如果代碼的動作取決於傳入的對象的類型,則需要進行某種類型的檢查。儘管如此,我建議儘量保持這種類型的檢查,然後去isinstance(x, str)

如果您傳入既不是字符串又不支持IO接口的對象,則會導致AttributeError。如果發生這種情況,這是調用代碼中的一個錯誤。這個異常不應該在任何地方處理 - 相反,錯誤應該修復!

這就是說,你可能使用

isinstance(x, io.IOBase) 

,以測試內置類支持I/O協議。這會限制你的代碼實際上從io.IOBase派生出來的類 - 這是一種膚淺和不必要的限制。

+0

我會補充說,檢查接口並不妨礙遵守的類不做它應該做的,使檢查相當多餘。 – XORcist 2011-05-02 14:08:44

+0

io.IOBase是一個抽象的基類,所以你不需要繼承isinstance/issubclass來工作 – 2011-05-02 14:14:09

+1

@gnibbler:你需要從它派生出來,或者你需要使用'IOBase註冊你自己的類型。寄存器()'。儘管如此,我認爲這是一個不必要的併發症,並不能帶來太多好處。 – 2011-05-02 14:17:53

1

或者我應該忘記檢查接口,只是讓一個可能的AttributeError進一步處理堆棧?

一般的pythonic原理似乎在做任何你想要處理的對象,只是捕獲它可能導致的異常。這就是所謂的鴨打字。儘管如此,這並不意味着你應該讓這些異常從你的函數中跳到調用代碼。如果能夠以有意義的方式做到這一點,您可以在函數本身中處理它們。

+0

+1爲正確的方式做到這一點! – jathanism 2011-05-02 14:04:29

0

是的,蟒蛇是關於鴨子打字,並且檢查幾個方法來判斷一個對象是否支持IO接口是完全可以接受的。有時甚至可以嘗試在try/except塊中調用你的方法,並且趕上TypeErrorValueError,這樣你就知道它是否真的支持相同的接口(但是很少使用)。我會說使用hasattr而不是看__class__.__dict__,但否則這是我會採取的方法。 (一般來說,我會首先檢查標準庫中是否有某個方法來處理類似的東西,因爲它可能會很容易出錯,以便自己決定組成「IO接口」的是什麼。例如,typesinspect模塊中有幾個方便的寶石用於相關的接口檢查。)

+0

噢,如果你決定把任何不是字符串的東西當作IO對象,確保使用'isinstance(x,types.StringTypes)',而不是'isinstance(x,str)',因爲'isinstance(u' foo',str)'返回'False'。 – 2011-05-02 14:14:24

+0

但第二個想法是,最後的警告不適用於Python 3,所以...繼續。 – 2011-05-02 14:17:13

+1

在Python 2.x中,你通常使用'isinstance(x,basestring)'來檢查'unicode'或'str'。 – 2011-05-02 14:20:53