類型檢查 創建類的實例時,該實例的類型為類本身: 要測試實例是否屬於某個類,可以使用type()內置函數: 當然,python中不建議如此檢查,更好的辦法是使用內置類型檢查函數isinstance(obj, cls): 同樣的,內置函數issubclass(cls1, cls2)可以用做子類的檢查: ...
類型檢查
創建類的實例時,該實例的類型為類本身:
class Foo(object): pass f = Foo()
要測試實例是否屬於某個類,可以使用type()內置函數:
>>> type(f) == Foo True
當然,python中不建議如此檢查,更好的辦法是使用內置類型檢查函數isinstance(obj, cls):
>>> isinstance(f, Foo) True
同樣的,內置函數issubclass(cls1, cls2)可以用做子類的檢查:
class SubFoo(Foo): pass >>> issubclass(SubFoo, Foo) True
這兩個內置函數的第二個參數可以是一個單獨的類,也可以是幾個類的元組:
>>> isinstance(1, (str, int)) True
以下兩個特殊方法可用於重新定義這兩個內置函數:
__instancecheck__(cls, obj)
__subclasscheck__(cls, subcls)
鴨子類型
python是門動態語言,調用對象的時候是不用考慮對象的類型的,而只需要對象有某些特定的屬性或者行為即可,也就是鴨子類型。
為了保持程式的鬆散耦合,我們可以不用繼承,而只是模仿一些對象的行為:
class File(object): def open(self): print 'open File' class FileLike(object): def open(self): print 'open FileLike' def foo(f): f.open()
只要定義了open()方法的實例,就可以作為函數foo()的參數:
>>> f = File() >>> fl = FileLike() >>> foo(f) open File >>> foo(fl) open FileLike
當我們測試對象是否可以作為foo()的參數時,要同時進行兩次isinstance()類型檢查,如果類型更多的話呢?
現在我們可以將File和FileLike分組並對其進行一次類型檢查:
class FileLikeClass(object): def __init__(self): self.reg = set() def register(self, cls): self.reg.add(cls) def __instancecheck__(self, obj): return self.__subclasscheck__(type(obj)) def __subclasscheck__(self, subcls): return any(cls in self.reg for cls in subcls.mro()) flc = FileLikeClass() flc.register(File) flc.register(FileLike)
上述代碼使用FileLikeClass創造一個對象,將要檢查的類型放入對象的一個集合屬性中,然後重新定義__instancecheck__(cls, obj)與__subclasscheck__(cls, subcls)方法,
進行子類檢查時,會檢查子類及子類的基類是否在集合中,進行類型檢查時,會檢查對象的類型是否是集合中的類的子類:
>>> f = File() >>> isinstance(f, flc) True >>> fl = FileLike() >>> isinstance(fl, flc) True
當然,python提供了一種更正式的機制來分組對象,定義介面併進行類型檢查----抽象基類
抽象基類
要定義抽象基類,需要使用abc模塊,該模塊定義一個元類(ABCMeta)和兩個裝飾器(@abstractmethod,抽象方法)與(@abstractproperty, 抽象特性),使用方法如下:
from abc import ABCMeta, abstractmethod, abstractproperty class AbsFile(object): __metaclass__ = ABCMeta @abstractmethod def open(self): pass
抽象基類就是定義各種方法而不做具體實現的類,抽象基類是無法實例化的:
>>> AbsFile() TypeError: Can't instantiate abstract class AbsFile with abstract methods open
任何繼承自抽象基類的類也必須實現這些方法,否則無法實例化:
class File(AbsFile): pass
實例化失敗:
>>> File() TypeError: Can't instantiate abstract class File with abstract methods open
需定義open方法:
class File(AbsFile): def open(self): print 'open File'
當然抽象基類中也可以對抽象方法進行具體實現,在子類中可以通過super()來調用:
class AbsFile(object): __metaclass__ = ABCMeta @abstractmethod def open(self): print 'open AbsFile' class File(AbsFile): def open(self): super(File, self).open() print 'open File'
實例化:
>>> File().open() open AbsFile open File
如果只想執行類型檢查的話,可以使用register()方法:
from abc import ABCMeta, abstractmethod, abstractproperty class AbsFile(object): __metaclass__ = ABCMeta @abstractmethod def open(self): pass class File(object): pass AbsFile.register(File)
向抽象基類註冊某個類時,是不會檢查該類是否實現了抽象基類的抽象方法或者特性的,只會影響類型檢查:
>>> f = File() >>> isinstance(f, AbsFile) True
python的collections模塊定義了與序列,集合和字典有關的各種抽象基類,numbers模塊定義了與數字有關的抽象基類。