From:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431865288798deef438d865e4c2985acff7e9fad15e3000
和靜態語言不同,Python允許對執行個體變數綁定任何資料,也就是說,對於兩個執行個體變數,雖然它們都是同一個類的不同執行個體,但擁有的變數名稱都可能不同:
>>> bart = Student('Bart Simpson', 59)>>> lisa = Student('Lisa Simpson', 87)>>> bart.age = 8>>> bart.age8>>> lisa.ageTraceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'Student' object has no attribute 'age'
如果要讓內部屬性不被外部存取,可以把屬性的名稱前加上兩個底線__,在Python中,執行個體的變數名如果以__開頭,就變成了一個私人變數(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))
在Python中,變數名類似__xxx__的,也就是以雙底線開頭,並且以雙底線結尾的,是特殊變數,特殊變數是可以直接存取的,不是private變數,所以,不能用__name__、__score__這樣的變數名。
有些時候,你會看到以一個底線開頭的執行個體變數名,比如_name,這樣的執行個體變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私人變數,不要隨意訪問”。
雙底線開頭的執行個體變數是不是一定不能從外部存取呢。其實也不是。不能直接存取__name是因為Python解譯器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數:
>>> bart._Student__name'Bart Simpson'
判斷一個變數是否是某個類型可以用isinstance()判斷:
a = list() # a是list類型b = Animal() # b是Animal類型c = Dog() # c是Dog類型
>>> isinstance(a, list)True>>> isinstance(b, Animal)True>>> isinstance(c, Dog)True
>>> isinstance(c, Animal)True
還可以判斷一個變數是否是某些類型中的一種,比如下面的代碼就可以判斷是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))True>>> isinstance((1, 2, 3), (list, tuple))True
python區別於C++的多態是不要關鍵字"virtual", 多態很好地遵循開閉原則
對擴充開放:允許新增Animal子類;
對修改封閉:不需要修改依賴Animal類型的run_twice()等函數。
靜態語言 vs 動態語言
對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法。
對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了:
class Timer(object): def run(self): print('Start...')
這就是動態語言的“鴨子類型”,它並不要求嚴格的繼承體系,一個對象只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。
Python的“file-like object“就是一種鴨子類型。對真正的檔案對象,它有一個read()方法,返回其內容。但是,許多個物件,只要有read()方法,都被視為“file-like object“。許多函數接收的參數就是“file-like object“,你不一定要傳入真正的檔案對象,完全可以傳入任何實現了read()方法的對象。 type()
基本類型都可以用type()判斷:
>>> type(123)<class 'int'>>>> type('str')<class 'str'>>>> type(None)<type(None) 'NoneType'>
如果一個變數指向函數或者類,也可以用type()判斷:
>>> type(abs)<class 'builtin_function_or_method'>>>> type(a)<class '__main__.Animal'>
>>> type(123)==type(456)True
>>> type('abc')==type(123)False
>>> type(123)==type(456)True>>> type(123)==intTrue>>> type('abc')==type('123')True>>> type('abc')==strTrue>>> type('abc')==type(123)False
判斷基礎資料型別 (Elementary Data Type)可以直接寫int,str等,但如果要判斷一個對象是否是函數怎麼辦。可以使用types模組中定義的常量:
>>> import types>>> def fn():... pass...>>> type(fn)==types.FunctionTypeTrue>>> type(abs)==types.BuiltinFunctionTypeTrue>>> type(lambda x: x)==types.LambdaTypeTrue>>> type((x for x in range(10)))==types.GeneratorTypeTrue
getattr()、setattr()以及hasattr()可以直接操作一個對象的狀態
>>> class MyObject(object):... def __init__(self):... self.x = 9... def power(self):... return self.x * self.x...>>> obj = MyObject()
緊接著,可以測試該對象的屬性:
>>> hasattr(obj, 'x') # 有屬性'x'嗎。True>>> obj.x9>>> hasattr(obj, 'y') # 有屬性'y'嗎。False>>> setattr(obj, 'y', 19) # 設定一個屬性'y'>>> hasattr(obj, 'y') # 有屬性'y'嗎。True>>> getattr(obj, 'y') # 擷取屬性'y'19>>> obj.y # 擷取屬性'y'19
如果試圖擷取不存在的屬性,會拋出AttributeError的錯誤:
>>> getattr(obj, 'z') # 擷取屬性'z'Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'MyObject' object has no attribute 'z'
可以傳入一個default參數,如果屬性不存在,就返回預設值:
>>> getattr(obj, 'z', 404) # 擷取屬性'z',如果不存在,返回預設值404404
也可以獲得對象的方法:
>>> hasattr(obj, 'power') # 有屬性'power'嗎。True>>> getattr(obj, 'power') # 擷取屬性'power'<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>