簡單點說,「面向對象」是一種編程範式,而編程範式是按照不一樣的編程特色總結出來的編程方式。俗話說,條條大路通羅馬,也就說咱們使用不一樣的方法均可以達到最終的目的,可是有些辦法比較快速、安全且效果好,有些方法則效率低下且效果不盡人意。一樣,編程也是爲了解決問題,而解決問題能夠有多種不一樣的視角和思路,前人把其中一些廣泛適用且行之有效的編程模式歸結爲「範式」。常見的編程範式有:html
1)分析出解決問題所須要的步驟;java
2)用函數把這些步驟一次實現;編程
3)一個一個地調用這些函數來解決問題;安全
1)把構成問題的事務分解、抽象成各個對象;模塊化
2)結合這些對象的共有屬性,抽象出類;函數式編程
3)類層次化結構設計--繼承 和 合成;函數
4)用類和實例進行設計和實現來解決問題。oop
關於面向對象編程 與 面向過程編程的區別與優缺點能夠參考這篇文章ui
面向對象編程達到了軟件工程的3個目標:重用性、靈活性、擴展性,而這些目標是經過如下幾個主要特色實現的:this
須要說明的是,Python不像Java中又專門的「接口」定義,Python中的接口與類沒有什麼區別,可是咱們能夠經過在一個用於當作接口的類中所定義的方法體中
raise NotImplementedError
異常,來強制子類必須從新實現該方法。
咱們知道,Python既能夠面向過程編程,也能夠面向對象編程。那麼什麼場景下應該使用面向對象編程呢?若是咱們僅僅是寫一個簡單的腳原本跑一些簡單的任務,咱們直接用面向過程編程就行了,簡單,快速。當咱們須要實現一個複雜的系統時,或者如下場景下,就須要使用面向對象編程:
封裝是面向對象的主要特徵之一,是對象和類概念的主要特性。簡單的說,一個類就是一個封裝了數據以及操做這些數據的方法的邏輯實體,它向外暴露部分數據和方法,屏蔽具體的實現細節。除此以外,在一個對象內部,某些數據或方法能夠是私有的,這些私有的數據或方法是不容許外界訪問的。經過這種方式,對象對內部數據提供了不一樣級別的保護以防止程序中無關的部分意外的改變或錯誤使用了對象的私有部分,好比java中修飾類變量和方法的相關關鍵字有:private、protected, public等。下面咱們經過類的定義和實例化的實例來講明一下Python中的是如何實現對這些不一樣等級數據的保護的。
類的定義是對顯示事務的抽象過程和能力,類是一個對象/實例的模板,也是一個特殊的對象/實例(由於Pythobn中一切皆對象,因此類自己也是一個對象)
如今咱們來定義個Person類,它有如下3個屬性:
假設如今咱們有如下幾個前提:
import uuid class Person(object): nationality = 'China' def __init__(self, name): self.name = name self.__id = str(uuid.uuid1()) def hello(self): print('Hi, i am %s, from %s, my id is %s' % (self.name, self.nationality, self.__id)) def get_and_print_id(self): print(self.__id) return self.__id
類實例化的方式:類名([參數...])
,參數是__init__方法中除了第一個self參數以外的其餘參數,上面定義的這個Person類中,實例化時須要傳遞的參數只有一個name。好比咱們來實例化3個Person對象,他們的name分別是 tom 和 jerry:
tom = Person('tom') jerry = Person('jerry') jack = Person('jack')
直接定義在class下的屬性就是公有屬性/類屬性,好比上面那個Person類中的nationality屬性。「公有」的意思是這個屬性是這個類的全部實例對象共同全部的,所以默認狀況下這個屬性值值保留一份,而不會爲該類的每一個實例都保存一份。
print(Person.nationality, tom.nationality, jerry.nationality, jack.nationality) tom.nationality = 'USA' print(Person.nationality, tom.nationality, jerry.nationality, jack.nationality) Person.nationality = 'India' print(Person.nationality, tom.nationality, jerry.nationality, jack.nationality)
輸出結果以下:
China China China China China USA China China India USA India India
結論:
- 公有屬性/靜態屬性 能夠直接經過類直接訪問,也能夠直接經過實例進行訪問; - 經過類的某個實例對公有屬性進行修改,實際上對爲該實例添加了一個與類的公有屬性名稱相同的成員屬性,對真正的公有屬性是沒有影響的,所以它不會影響其餘實例獲取的該公有屬性的值; - 經過類對公有屬性進行修改,必然是會改變公有屬性原有的值,他對該類全部的實例是都有影響的。
成員屬性,又稱成員變量 或 實例屬性,也就是說這些屬性是 該類的每一個實例對象單獨持有的屬性。成員屬性須要在類的__init__方法中進行聲明,好比上面的Person類中定義的name屬性就是一個成員屬性。
print(tom.name, jerry.name, jack.name) jerry.name = 'jerry01' print(tom.name, jerry.name, jack.name)
輸出結果:
tom jerry jack tom jerry01 jack
來看看能不能直接經過類訪問成員屬性
print(Person.name)
輸出結果:
Traceback (most recent call last): ... AttributeError: type object 'Person' has no attribute 'name'
結論:
- 成員屬性能夠直接經過實例對象來訪問和更改; - 成員屬性是每一個實例對象獨有的,某個實例對象的成員屬性被更改不會影響其餘實例對象的相同屬性的值; - 成員屬性的值不能經過類來訪問和修改;
私有屬性和成員屬性同樣,是在__init__方法中進行聲明,可是屬性名須要以雙下劃線__開頭,好比上面定義的Person中的__id屬性。私有屬性是一種特殊的成員屬性,它只容許在實例對象的內部(成員方法或私有方法中)訪問,而不容許在實例對象的外部經過實例對象或類來直接訪問,也不能被子類繼承。
經過實例對象訪問私有屬性:
print(tom.__id)
輸出結果
Traceback (most recent call last): ... AttributeError: 'Person' object has no attribute '__id'
經過類訪問私有屬性:
print(Person.__id)
輸出結果:
Traceback (most recent call last): ... AttributeError: type object 'Person' has no attribute '__id'
經過類的成員方法訪問私有屬性:
tom.hello() jerry.hello() jack.hello()
輸出結果:
Hi, i am tom, from China, my id is b6ac08c6-9dae-11e7-993f-208984d7aa83 Hi, i am jerry, from China, my id is b6ac08c7-9dae-11e7-b508-208984d7aa83 Hi, i am jack, from China, my id is b6ac08c8-9dae-11e7-9ace-208984d7aa83
結論:
- 私有變量不能經過類直接訪問; - 私有變量也不能經過實例對象直接訪問; - 私有變量能夠經過成員方法進行訪問。
那麼要訪問私有變量怎麼辦呢? 有兩種辦法:
辦法1:經過一個專門的成員方法返回該私有變量的值,好比上面定義的get_id()方法,搞過java的同窗很天然就會想到java類中的set和get方法。
tom_id = tom.get_id() jerry_id = jerry.get_id() jack_id = jack.get_id() print(tom_id, jerry_id, jack_id)
輸出結果:
46bc6b5c-9dd6-11e7-8306-208984d7aa83 46cbfe68-9dd6-11e7-b5d1-208984d7aa83 46cbfe69-9dd6-11e7-9b5c-208984d7aa83
辦法2:經過 實例對象._類名__私有變量名
的方式來訪問
print(tom._Person__id, jerry._Person__id, jack._Person__id)
輸出結果:
e1f4ee86-9dd6-11e7-a186-208984d7aa83 e1f5b1f8-9dd6-11e7-b1c3-208984d7aa83 e1f5b1f9-9dd6-11e7-b74a-208984d7aa83
繼成 和 組合 是類的兩個最主要的關係,而 繼承 關係的類之間是有層級的。被繼承的類被稱爲 父類、基類 或 超類 ;繼承的類被稱爲 子類 或 派生類。
繼承 是一個從通常到特殊的過程, 子類能夠繼承現有類的全部功能,而不須要從新實現代碼。簡單來講就是 繼承提升了代碼重用性和擴展性。
Python中類的繼承按照父類中的方法是否已實現可分爲兩種:
若是是根據要繼承的父類的個數來分,有能夠分爲:
class Person(object): def __init__(self, name, age): self.name = name self.age = age def walk(self): print('%s is walking...' % self.name) def talk(self): print('%s is talking...' % self.name )
class Teacher(Person): def __init__(self, name, age, level, salary): super(Teacher, self).__init__(name, age) self.level = level self.salary = salary def teach(self): print('%s is teaching...' % self.name)
class Student(Person): def __init__(self, name, age, class_): Person.__init__(self, name, age) self.class_ = class_ def study(self): print('%s is studying...' % self.name)
t1 = Teacher('張老師', 33, '高級教師', 20000) s1 = Student('小明', 13, '初一3班') t1.talk() t1.walk() t1.teach() s1.talk() s1.walk() s1.study()
輸出結果:
張老師 is talking... 張老師 is walking... 張老師 is teaching... 小明 is talking... 小明 is walking... 小明 is studying...
關於多繼承,以及多繼承時屬性查找順序(廣度優先、深度優先)的問題會在下面進行單獨說明。
多態是指,相同的成員方法名稱,可是成員方法的行爲(代碼實現)卻各不相同。這裏所說的多態是經過 繼承接口的方式實現的。Java中有interface,可是Python中沒有。Python中能夠經過在一個成員方法體中拋出一個NotImplementedError
異常來強制繼承該接口的子類在調用該方法前必須先實現該方法的功能代碼。
class Animal(object): def __init__(self, name): self.name = name def walk(self): raise NotImplemented('Subclass must implement the abstract method by self') def talk(self): raise NotImplemented('Subclass must implement the abstract method by self')
class Dog(Animal): pass
執行代碼:
dog = Dog('大黃') dog.talk()
輸出結果:
Traceback (most recent call last): ... raise NotImplemented('Subclass must implement the abstract method by self') TypeError: 'NotImplementedType' object is not callable
可見,此時子類必須本身先實現talk方法才能調用。
class Dog(Animal): def talk(self): print('%s is talking:旺旺...' % self.name) def walk(self): print('%s 是一條小狗,用4條腿走路' % self.name) class Duck(Animal): def talk(self): print('%s is talking: 嘎嘎...' % self.name) def walk(self): print('%s 是一隻鴨子,用兩條腿走路' % self.name)
執行代碼:
dog = Dog('大黃') dog.talk() dog.walk() duck = Duck('小白') duck.talk() duck.walk()
輸出結果:
大黃 is talking:旺旺... 大黃 是一條小狗,用4條腿走路 小白 is talking: 嘎嘎... 小白 是一隻鴨子,用兩條腿走路
由此可知:
上面提到過,類中封裝的是數據和操做數據的方法。數據就是屬性,且上面已經介紹過了屬性分爲:
公有屬性/類變量、成員屬性/實例變量 和 私有屬性。如今咱們來講說類中的方法,類中的方法分爲如下幾種:
成員方法: 上面定義的都是成員方法,一般狀況下,它們與成員屬性類似,是經過類的實例對象去訪問;成員方法的第一個參數必須是當前實例對象,一般寫爲self;實際上,咱們也能夠經過類名來調用成員方法,只是此時咱們須要手動的傳遞一個該類的實例對象給成員方法的self參數,這樣用明顯不是一種優雅的方法,所以基本不會這樣使用。
私有方法: 以雙下劃線開頭的成員方法就是私有方法,與私有屬性相似,只能在實例對象內部訪問,且不能被子類繼承;私有方法的第一個參數也必須是當前實例對象自己,一般寫爲self;
類方法: 以@classmethod來裝飾的成員方法就叫作類方法,它要求第一次參數必須是當前類。與公有屬性/靜態屬性 類似,除了可經過實例對象進行訪問,還能夠直接經過類名去訪問,且第一個參數表示的是當前類,一般寫爲cls;另外須要說明的是,類方法只能訪問公有屬性,不能訪問成員屬性,所以第一個參數傳遞的是表明當前類的cls,而不是表示實例對象的self。
靜態方法: 以@staticmethod來裝飾的成員方法就叫作靜態方法,靜態方法一般都是經過類名去訪問,且嚴格意義上來說,靜態方法已經與這個類沒有任何關聯了,由於靜態方法不要求必須傳遞實例對象或類參數,這種狀況下它不能訪問類中的任何屬性和方法。
屬性方法: 這個比較有意思,是指能夠像訪問成員屬性那樣去訪問這個方法;它的第一個參數也必須是當前實例對象,且該方法必需要有返回值。
咱們先來定義這樣一個類:
import uuid class Person(object): nationality = 'China' def __init__(self, name, age): self.name = name self.age = age self.__id = str(uuid.uuid1()) # 成員方法/實例方法 def sayHello(self): print('Hello, i am %s from %s, i am %d years old.' % (self.name, self.nationality, self.age)) # 私有方法 def __func0(self): print('private method: func0') print(self.name, self.age, self.__id, self.nationality) # 類方法 @classmethod def func1(cls): print(cls.nationality) # 靜態方法 @staticmethod def func2(a, b): print(a + b) # 屬性方法 @property def func3(self): return '%s: %d' % (self.name, self.age)
執行代碼:
p = Person('Tom', 18) p.sayHello() Person.sayHello(p) Person.func1() p.func1() Person.func2(3, 4) p.func2(3, 4) print(p.func3)
輸出結果:
Hello, i am Tom from China, i am 18 years old. Hello, i am Tom from China, i am 18 years old. China China 7 7 Tom: 18
總結:
- 成員方法也能夠經過類名去訪問,可是有點畫蛇添足的感受;
- 類方法和靜態方法也能夠經過實例對象去訪問,可是一般狀況下都是經過類名直接訪問的;
- 最重要的一條總結:類的各類方法,能訪問哪些屬性其實是跟方法的參數有關的:
- 好比成員方法要求第一個參數必須是一個該類的實例對象,那麼實例對象能訪問的屬性,成員方法都能訪問,並且還能訪問私有屬性;
- 再好比,類方法要求第一個參數必須是當前類,所以它只能訪問到類屬性/公有屬性,而訪問不到成員屬性 和 私有屬性;
- 再好比,靜態方法對參數沒有要求,也就意味着咱們能夠任意給靜態方法定義參數;假如咱們給靜態方法定義了表示當前類的參數,那麼就能夠訪問類屬性/公有屬性;假如咱們給靜態方法定義了表示當前類的實例對象的參數,那麼就能夠訪問成員屬性;假如咱們沒有給靜態方法定義這兩個參數,那麼就不能訪問該類或實例對象的任何屬性。
咱們上面提到過:名稱以雙下劃線__開頭的屬性是私有屬性,名稱以雙下劃線__開頭的方法是私有方法。這裏咱們要來講明的是,Python的類中有一些內置的、特殊的屬性和方法,它們的名稱是以雙下劃線__開頭,同時又以雙下劃線__結尾。這些屬性和方法再也不是私有屬性和私有方法,它們是能夠在類的外部經過實例對象去直接訪問的,且它們都有着各自特殊的意義,咱們能夠經過這些特殊屬性和特殊方法來獲取一些重要的信息,或執行一些有用的操做。
屬性名稱 | 說明 |
---|---|
__doc__ | 類的描述信息 |
__module__ | 表示當前操做的對象對應的類的定義所在的模塊名 |
__class__ | 表示當前操做的對象對應的類名 |
__dict__ | 一個字典,保存類的全部的成員(包括屬性和方法)或實例對象中的全部成員屬性 |
如今來看一個實例:
在dog.py模塊定義一個Dog類
class Dog(object): """這是一個Dog類""" # print('Hello, This is a dog.') color = '白色' def __init__(self, name): self.name = name self.__id = 1234 def func1(self): pass def __func1(self): pass @classmethod def func2(cls): pass @staticmethod def func3(): pass
在test.py模塊執行下面的代碼
from dog import Dog dog1 = Dog('泰迪') print(dog1.__doc__) print(dog1.__module__) print(dog1.__class__) print(dog1.__dict__) print(Dog.__dict__)
輸出結果
這是一個Dog類 dog <class 'dog.Dog'> {'name': '泰迪', '_Dog__id': 1234} {'__dict__': <attribute '__dict__' of 'Dog' objects>, '__module__': 'dog', 'func2': <classmethod object at 0x000001DF0C658F98>, 'color': '白色', 'func3': <staticmethod object at 0x000001DF0C658FD0>, '_Dog__func1': <function Dog.__func1 at 0x000001DF0C63E400>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': '這是一個Dog類', '__init__': <function Dog.__init__ at 0x000001DF0C63E2F0>, 'func1': <function Dog.func1 at 0x000001DF0C63E378>}
總結:
方法名稱 | 說明 |
---|---|
__init__ | 類構造方法,經過類建立對象時會自動觸發執行該方法 |
__del__ | 析構方法,當對象在內存中被什邡市,會自動觸發執行該方法。好比實例對象的做用域退出時,或者執行 del 實例對象 操做時。 |
__str__ | 若是一個類中定義了__str__方法,那麼在打印對象時默認輸出該方法的返回值,不然會打印出該實例對象的內存地址。 |
__xxxitem__ | 是指__getitem__、__setitem__、__delitem這3個方法,它們用於索引操做,好比對字典的操做,分別表示 獲取、設置、刪除某個條目。 數據。能夠經過這些方法來定義一個類對字典進行封裝,從而能夠對字典中key的操做進行控制,尤爲是刪除操做。 |
__new__ | 該方法會在__init__方法以前被執行,該方法會建立被返回一個新的實例對象,而後傳遞給__init__。另外須要說明的是,這不是一個成員方法,而是一個靜態方法。 |
__call__ | 源碼中的註釋是"Call self as a function." 意思是把本身(實例對象)做爲一個函數去調用,而函數的調用方式是函數名() 。也就是說,當咱們執行實例對象() 或者 類名()() 這樣的操做時會觸發執行該方法。 |
先來定義這樣一個類:
class Person(object): def __call__(self, *args, **kwargs): print(self.name, '__call__') def __init__(self, name, age): self.name = name self.age = age print(self.name, '__init__') def __del__(self): print(self.name, '__del__') def __str__(self): print(self.name, '__str__') return '%s: %d'% (self.name, self.age)
執行下面的代碼:
print('--------實例化對象-----------') p = Person('Tom', 18) print('--------打印實例對象-----------') print(p) print('--------把實例對象做爲方法進行調用-----------') p() # 等價於 Person('Tom', 18)() print('--------程序運行結束-----------')
輸出結果:
--------實例化對象----------- Tom __init__ --------打印實例對象----------- Tom __str__ Tom: 18 --------把實例對象做爲方法進行調用----------- Tom __call__ --------程序運行結束----------- Tom __del__
能夠看到,全部代碼都執行完後,進程退出時實例對象的__del__方法才被調用,這是由於對象要被銷燬了。
定義一個相似字典的類
class MyDict(object): def __init__(self, init=None): self.__dict = init if init is not None else {} def __setitem__(self, key, value): print('__setitem__', key) self.__dict[key] = value def __getitem__(self, item): print('__getitem__', item) return self.__dict.get(item, None) def __delitem__(self, key): print('__delitem__', key) if key is not None and key.startswith('wh'): print('You can not delete this item ') return None return self.__dict.pop(key, None)
執行下面的代碼
# 類實例化與get item my_dict = MyDict(init={'what': '打豆豆', 'who': '企鵝團', 'time': '吃飽睡好以後'}) print(my_dict['who'], my_dict['time'], my_dict['what']) # set item my_dict['num'] = '10次' print(my_dict['who'], my_dict['time'], my_dict['what'], my_dict['num']) # del item del my_dict['num'] print(my_dict['num']) del my_dict['what'] print(my_dict['what'])
輸出結果
__getitem__ who __getitem__ time __getitem__ what 企鵝團 吃飽睡好以後 打豆豆 __setitem__ num __getitem__ who __getitem__ time __getitem__ what __getitem__ num 企鵝團 吃飽睡好以後 打豆豆 10次 __delitem__ num __getitem__ num None __delitem__ what You can not delete this item __getitem__ what 打豆豆
可見,若是一個類實現了__setitem__,__getitem、__delitem 這幾個方法,就能夠執行一些相似字典同樣的操做,好比上面用到的:
my_dict['KEY']
會自動調用my_dict實例對象的__getitem__方法;my_dict['KEY'] = VALUE
會自動調用my_dict實例對象的__setitem__方法;del my_dict['KEY']
會自動調用my_dict實例獨享的__delitem__方法;
而咱們定義這樣一個類的目的在於,咱們能夠更好對字典操做進行控制,好比上面的例子中咱們不容許刪除key以'wh'開頭的條目。
有的同窗說:「這個還不簡單嗎?子類確定是先找本身有沒有這個屬性或方法,有的話直接調用本身的,沒有再去父類裏面找。」 沒毛病,確實是這樣的,可是咱們真的理解這句話了嗎?另外,若是是多繼承且有多個類的層級關係,查找順序又是怎樣的呢?再來簡單描述下這裏要討論的問題是什麼,簡單來講,就是要你們搞明白2個問題:
若是你們把這2問題搞明白了,這裏目的就達到了。下面咱們分別以單繼承和多繼承兩個實例來說解咱們這裏要說明的問題。
A、B、C三個類的定義以下:
class A(object): def __init__(self, name): self.name = name def func1(self): print('class A: func1') def func2(self): print('class A: func2') class B(A): def __init__(self, name, age): super(B, self).__init__(name) self.age = age def func2(self): print('class B: func2') class C(A): def func1(self): print('class C: func1') def func3(self): print('class C: func3')
如今要執行這段代碼:
objB = B('Tom', 18) objC = C('Jerry') print(objB.name, objB.age) print(objC.name) objB.func1() objC.func1()
請先思考下面代碼的輸出結果,再去看下面的答案。
輸出結果:
Tom 18 Jerry class A: func1 class C: func1
這是最簡單的狀況,也是最容易用開頭那段話來解釋清楚的狀況,所以不作過多贅述。但這只是個引子,真正要討論的是下面這種狀況:
A與B兩個類的定義以下:
class A(object): def __init__(self, name): self.name = name def func1(self): print('class A: func1') self.func2() def func2(self): print('class A: func2') class B(A): def __init__(self, name, age): super(B, self).__init__(name) self.age = age def func2(self): print('class B: func2')
如今要執行下面的代碼:
objB = B('Tom', 18) objB.func1()
你內心有答案了嗎?func1必然是執行的父類 A中的func1,由於子類B中沒有這個方法。那麼父類func1中調用的func2方法,究竟是父類的仍是子類的呢?解決了這個疑問,答案天然就出來了。
class B 是class A的子類,所以它會繼承class A的的方法func1和func2。可是,class B已經重寫了func2,能夠理解爲class A中的func2方法已經被覆蓋了,class B如今只能看到本身重寫後的那個func2方法,因此func1中調用的應該是class B 重寫後的func2方法。
有的同窗可能不太能理解,class A中的方法怎麼能調用class B中的方法呢?下面咱們來看下class B與class
A的包含關係圖:
由於子類 class B繼承了 class A的內容,所以綠框中的內容(class A)是屬於藍框(class B)中的一部分,他們應該看作一個總體。綠框中的func1是能夠調用綠框外的func2的,由於他們都是objB中的成員方法。其實理解這些以後,如今咱們來套用開始那句話:
所以上面這段代碼的執行結果是:
class A: func1 class B: func2
Python 2.2引入了新式類,與它對應的是經典類,這裏咱們僅僅是解釋下他們的概念,爲講解下面的內容作鋪墊,不會深刻討論的它們的之間的區別。這裏咱們主要說明一下幾個點就能夠了:
深度優先 能夠理解爲 縱向優先,廣度優先 能夠理解爲 水平方法優先。咱們知道,類與類之間是有層級關係的,父類與子類是縱向的層級關係,同一個父類的多個直接子類是水平方向的同級關係。
上圖中 A是父類、B和C是繼承A的子類,D是同時繼承B和C的子類。此時D的一個實例對象去查找一個父類中的屬性或方法的查找順序就有兩種可能,可是這兩種查找順序中第一個查找的父類必然都是B:
定義如下幾個類:
class A(object): def func1(self): print('class A: func1') def func2(self): print('class A: func2') class B(A): def func3(self): print('class B: func3') class C(A): def func1(self): print('class C: func1') class D(B, C): pass
執行以下代碼:
objD = D() objD.func1()
Python 2.7 和 Python 3.5的輸出結果都是同樣的:
class C: func1
咱們更改下A的定義,不顯示的指定其繼承object:
class A(): def func1(self): print('class A: func1') def func2(self): print('class A: func2') class B(A): def func3(self): print('class B: func3') class C(A): def func1(self): print('class C: func1') class D(B, C): pass
再來執行一樣的代碼:
objD = D() objD.func1()
Python 2.7的輸出結果:
class A: func1
Python 3.5的輸出結果:
class C: func1
前面咱們已經說過了 在Python 3.x中不管是否顯示指定繼承object,全部的類都是新式類,那麼咱們根據上面的兩個實例的輸出結果能夠得出這樣的結論:在多繼承的狀況下,經典類查找父類屬性或方法的順序是深度優先,新式類查找父類屬性的順序是廣度優先。
因爲篇幅問題,還有幾個與類相關的話題會單獨再寫一篇,好比類的實例化過程、反射以及異常類的介紹和使用等。
問題交流羣:666948590