標籤:
繼承和多態
在OOP程式設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Superclass)。
>>> class Animal(object):#名為Animal的class
defrun(self):
print‘Animal is running...‘
>>> class Dog(Animal):#從Animal類繼承
pass
>>> dog=Dog()
>>> dog.run()#子類獲得了父類的全部功能
Animal is running...
>>> class Dog(Animal):#對子類增加一些方法
defrun(self):
print‘Dog is running...‘
>>> dog=Dog()
>>> dog.run()
Dog is running...
多態
a = list() # a是list類型
b = Animal() # b是Animal類型
c = Dog() # c是Dog類型
>>> isinstance(a, list)
True
在繼承關係中,如果一個執行個體的資料類型是某個子類,那它的資料類型也可以被看做是父類。但是,反過來就不行:
>>> b = Animal()
>>> isinstance(b, Dog)
False
繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫;有了繼承,才能有多態。在調用類執行個體方法的時候,盡量把變數視作父類類型,這樣,所有子類類型都可以正常被接收;
使用type()
判斷物件類型,使用type()函數:
>>> type(123)#基本類型都可以用type()判斷
<type ‘int‘>
>>> type(‘str‘)
<type ‘str‘>
>>> type(None)
<type ‘NoneType‘>
>>> type(abs)#變數指向函數或者類,也可以用type()判斷
<type ‘builtin_function_or_method‘>
>>> type(a)
<class ‘__main__.Animal‘>
>>> type(123)==type(456)#比較兩個變數的type類型是否相同
True
>>> import types#Python把每種type類型都定義好了常量,放在types模組裡
>>> type(‘abc‘)==types.StringType
True
>>> type(u‘abc‘)==types.UnicodeType
True
>>> type([])==types.ListType
True
>>> type(str)==types.TypeType
True
>>> type(int)==type(str)==types.TypeType#特殊的類型
True
使用isinstance()
isinstance()可以告訴我們,一個對象是否是某種類型。他判斷的是一個對象是否是該類型本身,或者位於該類型的父繼承鏈上。
>>> isinstance(d, Dog) and isinstance(d,Animal)
True
>>> isinstance(‘a‘, str)
True#能用type()判斷的基本類型也可以用isinstance()判斷
>>> isinstance(‘a‘, (str, unicode))
True# 判斷一個變數是否是某些類型中的一種
>>> isinstance(u‘a‘, basestring)
True#str和unicode都是從basestring繼承下來的
使用dir()
>>> dir(‘ABC‘)#獲得一個str對象的所有屬性和方法
[‘__add__‘, ‘__class__‘, ‘__contains__‘,‘__delattr__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘,‘__getitem__‘, ‘__getnewargs__‘, ‘__getslice__‘, ‘__gt__‘, ‘__hash__‘,‘__init__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mod__‘, ‘__mul__‘, ‘__ne__‘,‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__rmod__‘, ‘__rmul__‘,‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘_formatter_field_name_split‘,‘_formatter_parser‘, ‘capitalize‘, ‘center‘, ‘count‘, ‘decode‘, ‘encode‘,‘endswith‘, ‘expandtabs‘, ‘find‘, ‘format‘, ‘index‘, ‘isalnum‘, ‘isalpha‘,‘isdigit‘, ‘islower‘, ‘isspace‘, ‘istitle‘, ‘isupper‘, ‘join‘, ‘ljust‘, ‘lower‘,‘lstrip‘, ‘partition‘, ‘replace‘, ‘rfind‘, ‘rindex‘, ‘rjust‘, ‘rpartition‘,‘rsplit‘, ‘rstrip‘, ‘split‘, ‘splitlines‘, ‘startswith‘, ‘strip‘, ‘swapcase‘,‘title‘, ‘translate‘, ‘upper‘, ‘zfill‘]
類似__xxx__的屬性和方法在Python中都是有特殊用途的,比如__len__方法返回長度。在Python中,如果你調用len()函數試圖擷取一個對象的長度,實際上,在len()函數內部,它自動去調用該對象的__len__()方法,所以,下面的代碼是等價的:
>>> len(‘ABC‘)
3
>>> ‘ABC‘.__len__()
3
我們自己寫的類,如果也想用len(myObj)的話,就自己寫一個__len__()方法:
>>> class MyObject(object):
def__len__(self):
return 100
>>> obj = MyObject()
>>> len(obj)
100
配合getattr()、setattr()以及hasattr(),我們可以直接操作一個對象的狀態:
class MyObject(object):
def__init__(self):
self.x = 9
defpower(self):
return self.x * self.x
>>> hasattr(obj, ‘x‘) # 有屬性‘x‘嗎?
True
>>> obj.x
9
>>> hasattr(obj, ‘y‘) # 有屬性‘y‘嗎?
False
>>> setattr(obj, ‘y‘, 19) # 設定一個屬性‘y‘
>>> hasattr(obj, ‘y‘) # 有屬性‘y‘嗎?
True
>>> getattr(obj, ‘y‘) # 擷取屬性‘y‘
19
>>> obj.y # 擷取屬性‘y‘
19
>>> getattr(obj, ‘z‘, 404) # 擷取屬性‘z‘,如果不存在,返回預設值404
404
>>> hasattr(obj, ‘power‘) # 有屬性‘power‘嗎?
True
>>> getattr(obj, ‘power‘) # 擷取屬性‘power‘
<bound method MyObject.power of<__main__.MyObject object at 0x108ca35d0>>
>>> fn = getattr(obj, ‘power‘) # 擷取屬性‘power‘並賦值到變數fn
>>> fn # fn指向obj.power
<bound method MyObject.power of<__main__.MyObject object at 0x108ca35d0>>
>>> fn() # 調用fn()與調用obj.power()是一樣的
81
使用__slots__
>>> class Student(object):
pass
>>> s = Student()
>>> s.name = ‘Michael‘ # 動態給執行個體綁定一個屬性
>>> print s.name
Michael
>>> def set_age(self, age): # 定義一個函數作為執行個體方法
self.age = age
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s,Student) # 給執行個體綁定一個方法
>>> s.set_age(25) # 調用執行個體方法
>>> s.age # 測試結果
25
但是,給一個執行個體綁定的方法,對另一個執行個體是不起作用的:
>>> s2 = Student() # 建立新的執行個體
>>> s2.set_age(25) # 嘗試調用方法
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
AttributeError: ‘Student‘ object has no attribute‘set_age‘
為了給所有執行個體都Binder 方法,可以給classBinder 方法:
>>> def set_score(self, score):
... self.score = score
...
>>> Student.set_score = MethodType(set_score,None, Student)
給classBinder 方法後,所有執行個體均可調用:
>>> s.set_score(100)
>>> s.score
100
>>> s2.set_score(99)
>>> s2.score
99
為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的__slots__變數,來限制該class能添加的屬性:
>>> class Student(object):
__slots__ = (‘name‘, ‘age‘) # 用tuple定義允許綁定的屬性名稱
>>> s = Student() # 建立新的執行個體
>>> s.name = ‘Michael‘ # 綁定屬性‘name‘
>>> s.age = 25 # 綁定屬性‘age‘
>>> s.score = 99 # 綁定屬性‘score‘
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
AttributeError: ‘Student‘ object has no attribute‘score‘
由於‘score‘沒有被放到__slots__中,所以不能綁定score屬性,試圖綁定score將得到AttributeError的錯誤。
使用__slots__要注意,__slots__定義的屬性僅對當前類起作用,對繼承的子類是不起作用的:
>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999
除非在子類中也定義__slots__,這樣,子類允許定義的屬性就是自身的__slots__加上父類的__slots__。
Python學習 Day 8 繼承 多態 Type isinstance dir __slots__