1. 程式人生 > >python的介面和抽象類

python的介面和抽象類

有些面向物件的語言,如JAVA,支援介面,可以宣告一個支援給定的一些方法方法,或者支援給定存取協議的類。抽象基類(或者ABCs)是Python裡一個相同的特性。抽象基類由abc模組構成,包含了一個叫做ABCMeta的metaclass。這個metaclass由內建的isinstance()和issubclass()特別處理,幷包含一批會被Python開發人員廣泛用到的基礎抽象基類。將來的Python版本可能會加入更多的抽象基類。

比如說有某個特定類你想知道它是否支援dictionary型別的存取。然而dictionary型別是個模糊的表述。它可能意味著可以通過obj[1]進行存取。那是否也意味著obj[2]= value這種賦值也起作用呢?又或者該物件將具備keys(), values()和items()方法?迭代的變種如iterkeys(),copy()和update()又如何呢?通過物件迭代的iter()呢?

Python 2.6的collections模組包括了許多不同的抽象基類來表示出這些不同。Iterable表明一個類定義了__iter__(),Container意味著該類定義了__contains__()方法,因此支援x in y表示式。基本的dictionary介面包括存取資料和keys(),values(),以及items(),由MutableMapping抽象基類定義。

你可以讓你的類繼承某個特定的抽象基類,來表示它們支援抽象基類介面:

import collections
class Storage(collections.MutableMapping):

 ...

另外,你可以不繼承基類,以呼叫抽象基類的register()方法的方式註冊該類。

import collections
class Storage:

 ...


collections.MutableMapping.register(Storage)

相對於你寫的類來說,從抽象基類繼承可能更清晰。當你已經寫了一個新的抽象基類,能描述一個存在的型別或類,或者你想宣告某些第三方類實現了一個抽象基類,
register()方法是有用的。例如,如果你定義了一個PrintableType抽象基類,以下是合法的:

# Register Python's types

PrintableType.register(int)

PrintableType.register(float)

PrintableType.register(str)

類應當遵循由抽象基類指明的語義,但是Python不能檢查這一點;類作者應該理解抽象基類的需求,並據此實現程式碼。

要檢查一個物件是否支援某個特定介面,你可以這樣寫:

def func(d):

  if not isinstance(d, collections.MutableMapping):

    raise ValueError("Mapping object expected, not %r" % d)

不要認為你必須像上面的例子那樣,寫許多檢查性的程式碼。Python有很強的duck-typing傳統,從來不會進行顯式的型別檢查。程式碼只是簡單的呼叫物件的方法,認為這些方
法會存在,如果不存在就會丟擲異常。抽象基類檢查時一定要謹慎,最好在非常必要的時候才那樣做。

你可以在類的定義中用abc.ABCMeta作為metaclass寫自己的抽象基類:

from abc import ABCMeta, abstractmethod


class Drawable():

  __metaclass__ = ABCMeta
         
  @abstractmethod

  def draw(self, x, y, scale=1.0):

    pass


  def draw_doubled(self, x, y):

    self.draw(x, y, scale=2.0)



class Square(Drawable):

  def draw(self, x, y, scale):

    ...

在以上的Drawable抽象基類中,draw_doubled()方法會按物件兩倍的大小畫出來,並且可以呼叫Drawable自己的方法來實現。因此實現這個抽象基類的類不需要
提供它們自己的draw_doubled()實現,儘管他們可以那樣做。然而draw()的實現是必須的;抽象基類不能提供一個有用的一般實現。

你可以在必須實現的方法,如draw()中應用@abstractmethod修飾符;Python會對那些沒有定義該方法的類丟擲異常。注意,只有當你試圖建立一個子類例項,但是卻缺少該方法的時候才會丟擲異常。

>>>class Circle(Drawable):

...   pass

...

>>>c=Circle()
Traceback (most recent call last):

 File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Circle with abstract methods draw

>>>

抽象資料屬性可以用@abstractproperty decorator宣告:

from abc import abstractproperty

 ...


@abstractproperty

def readonly(self):

  return self._x

子類必須定義一個readonly()屬性。

===================================

java中的抽象類是為了實現c++中的抽象類和模板之類的東西
介面是為了解決java不能多繼承的問題

很顯然樓主是從java才開始接觸面向物件程式設計的。實際上java的“介面”是一個特例而非普通現象。如果可以多繼承的話,那還要接口乾什麼?

實際上python才是最符合現實邏輯的“面向物件”
python允許多繼承,正如現實中,你既是公民也是納稅人,我們直接使用這些“類”而不需要特別的建立什麼“納稅人介面”
python中所有的類,都是抽象類,或者說根本不存在抽象類,類方法可以直接使用,“類”本身在定義的時候就已經例項化,你可以通過輸入:某類[回車]看到其記憶體控制代碼。這是符合事實的,並且時簡約明瞭的。
而在C++和java當中,一個類定義了以後,肯定是佔用了記憶體空間,但是同時他又沒有例項化,如果要使用的話還得例項化一次,又要佔用一些記憶體空間。而類定義所佔用的記憶體空間,使用率很低。
python中不存在“基類”的概念,也沒有單根,更沒有基本型別,所有的一切都是物件。
python是無神論的最完美體現,沒有亞當,沒有上帝,沒有鬼神,沒有唯一的主。你愛信什麼信什麼,愛是什麼是什麼,沒有任何約束,但是不能存在特殊。

另外,python根本沒有意去模仿java的介面,因為那完全沒必要,python的標準類就完全包含java中的介面的所有功能。倒是模仿一下c++的模板會有些實際用途。

====================================

python介面的例子

twisted的twisted\internet\interface.py裡使用zope.interface

來源:http://www.cnblogs.com/dkblog/archive/2011/08/03/2125911.html