python之路-面向對象

編程范式

編程是 程序 員 用特定的語法+數據結構+算法組成的代碼來告訴計算機如何執行任務的過程 , 一個程序是程序員為了得到一個任務結果而編寫的一組指令的集合,正所謂條條大路通羅馬,實現一個任務的方式有很多種不同的方式, 對這些不同的編程方式的特點進行歸納總結得出來的編程方式類別,即為編程范式。 不同的編程范式本質上代表對各種類型的任務采取的不同的解決問題的思路, 大多數語言只支持一種編程范式,當然也有些語言可以同時支持多種編程范式。 兩種最重要的編程范式分別是面向過程編程和面向對象編程。

面向過程編程(Procedural Programming) Procedural programming uses a list of instructions to tell the computer what to do step-by-step. 面向過程編程依賴 - 你猜到了- procedures,一個procedure包含一組要被進行計算的步驟, 面向過程又被稱為top-down languages, 就是程序從上到下一步步執行,一步步從上到下,從頭到尾的解決問題 。基本設計思路就是程序一開始是要著手解決一個大的問題,然后把一個大問題分解成很多個小問題或子過程,這些子過程再執行的過程再繼續分解直到小問題足夠簡單到可以在一個小步驟范圍內解決。

舉個典型的面向過程的例子, 數據庫備份, 分三步,連接數據庫,備份數據庫,測試備份文件可用性。

代碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def db_conn():
????print("connecting db...")
def db_backup(dbname):
????print("導出數據庫...",dbname)
????print("將備份文件打包,移至相應目錄...")
def db_backup_test():
????print("將備份文件導入測試庫,看導入是否成功")
def main():
????db_conn()
????db_backup('my_db')
????db_backup_test()
if __name__ == '__main__':
????main()

  

這樣做的問題也是顯而易見的,就是如果你要對程序進行修改,對你修改的那部分有依賴的各個部分你都也要跟著修改, 舉個例子,如果程序開頭你設置了一個變量值 為1 , 但如果其它子過程依賴這個值 為1的變量才能正常運行,那如果你改了這個變量,那這個子過程你也要修改,假如又有一個其它子程序依賴這個子過程 , 那就會發生一連串的影響,隨著程序越來越大, 這種編程方式的維護難度會越來越高。 所以我們一般認為, 如果你只是寫一些簡單的腳本,去做一些一次性任務,用面向過程的方式是極好的,但如果你要處理的任務是復雜的,且需要不斷迭代和維護 的, 那還是用面向對象最方便了。

面向對象編程

OOP編程是利用“類”和“對象”來創建各種模型來實現對真實世界的描述,使用面向對象編程的原因一方面是因為它可以使程序的維護和擴展變得更簡單,并且可以大大提高程序開發效率 ,另外,基于面向對象的程序可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。

面向對象的幾個核心特性如下

Class 類 一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法

Object 對象 一個對象即是一個類的實例化后實例,一個類必須經過實例化后方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之前有共性,亦有不同

Encapsulation 封裝 在類中對數據的賦值、內部調用對外部用戶是透明的,這使類變成了一個膠囊或容器,里面包含著類的數據和方法

Inheritance 繼承 一個類可以派生出子類,在這個父類里定義的屬性、方法自動被子類繼承

Polymorphism 多態 多態是面向對象的重要特性,簡單點說:“一個接口,多種實現”,指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。 編程其實就是一個將具體世界進行抽象化的過程,多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物進行對話。 對不同類的對象發出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至于每個員工,當然會各司其職,做各自的工作。 多態允許將子類的對象當作父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這里引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象可以在運行期間動態綁定

面向對象編程(Object-Oriented Programming?)介紹

對于編程語言的初學者來講,OOP不是一個很容易理解的編程方式,大家雖然都按老師講的都知道OOP的三大特性是繼承、封裝、多態,并且大家也都知道了如何定義類、方法等面向對象的常用語法,但是一到真正寫程序的時候,還是很多人喜歡用函數式編程來寫代碼,特別是初學者,很容易陷入一個窘境就是“我知道面向對象,我也會寫類,但我依然沒發現在使用了面向對象后,對我們的程序開發效率或其它方面帶來什么好處,因為我使用函數編程就可以減少重復代碼并做到程序可擴展了,為啥子還用面向對象?”。 對于此,我個人覺得原因應該還是因為你沒有充分了解到面向對象能帶來的好處,今天我就寫一篇關于面向對象的入門文章,希望能幫大家更好的理解和使用面向對象編程。 ?
無論用什么形式來編程,我們都要明確記住以下原則:
  1. 寫重復代碼是非常不好的低級行為
  2. 你寫的代碼需要經常變更?
開發正規的程序跟那種寫個運行一次就扔了的小腳本一個很大不同就是,你的代碼總是需要不斷的更改,不是修改bug就是添加新功能等,所以為了日后方便程序的修改及擴展,你寫的代碼一定要遵循易讀、易改的原則(專業數據叫可讀性好、易擴展)。
如果你把一段同樣的代碼復制、粘貼到了程序的多個地方以實現在程序的各個地方調用 這個功能,那日后你再對這個功能進行修改時,就需要把程序里多個地方都改一遍,這種寫程序的方式是有問題的,因為如果你不小心漏掉了一個地方沒改,那可能會導致整個程序的運行都 出問題。 因此我們知道 在開發中一定要努力避免寫重復的代碼,否則就相當于給自己再挖坑。
還好,函數的出現就能幫我們輕松的解決重復代碼的問題,對于需要重復調用的功能,只需要把它寫成一個函數,然后在程序的各個地方直接調用這個函數名就好了,并且當需要修改這個功能時,只需改函數代碼,然后整個程序就都更新了。
其實OOP編程的主要作用也是使你的代碼修改和擴展變的更容易,那么小白要問了,既然函數都能實現這個需求了,還要OOP干毛線用呢? 呵呵,說這話就像,古時候,人們打仗殺人都用刀,后來出來了槍,它的主要功能跟刀一樣,也是殺人,然后小白就問,既然刀能殺人了,那還要槍干毛線,哈哈,顯而易見,因為槍能更好更快更容易的殺人。函數編程與OOP的主要區別就是OOP可以使程序更加容易擴展和易更改。
小白說,我讀書少,你別騙我,口說無憑,證明一下,好吧,那我們就下面的例子證明給小白看。?
相信大家都打過CS游戲吧,我們就自己開發一個簡單版的CS來玩一玩。?
暫不考慮開發場地等復雜的東西,我們先從人物角色下手, 角色很簡單,就倆個,恐怖份子、警察,他們除了角色不同,其它基本都 一樣,每個人都有生命值、武器等。 咱們先用非OOP的方式寫出游戲的基本角色?
?
1
2
3
4
5
6
7
8
9
10
11
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100

上面定義了一個恐怖份子Alex和一個警察Jack,但只2個人不好玩呀,一干就死了,沒意思,那我們再分別一個恐怖分子和警察吧,
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
money = 10000
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
money2 = 10000
#role 3
name3 = 'Rain'
role3 = 'terrorist'
weapon3 = 'C33'
life_value3 = 100
money3 = 10000
#rolw 4
name4 = 'Eric'
role4 = 'police'
weapon4 = 'B51'
life_value4 = 100
money4 = 10000

4個角色雖然創建好了,但是有個問題就是,每創建一個角色,我都要單獨命名,name1,name2,name3,name4…,后面的調用的時候這個變量名你還都得記著,要是再讓多加幾個角色,估計調用時就很容易弄混啦,所以我們想一想,能否所有的角色的變量名都是一樣的,但調用的時候又能區分開分別是誰??
當然可以,我們只需要把上面的變量改成字典的格式就可以啦。?
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
roles = {
????1:{'name':'Alex',
???????'role':'terrorist',
???????'weapon':'AK47',
???????'life_value': 100,
???????'money': 15000,
???????},
????2:{'name':'Jack',
???????'role':'police',
???????'weapon':'B22',
???????'life_value': 100,
????????'money': 15000,
???????},
????3:{'name':'Rain',
???????'role':'terrorist',
???????'weapon':'C33',
???????'life_value': 100,
???????'money': 15000,
???????},
????4:{'name':'Eirc',
???????'role':'police',
???????'weapon':'B51',
???????'life_value': 100,
???????'money': 15000,
???????},
}
print(roles[1]) #Alex
print(roles[2]) #Jack

很好,這個以后調用這些角色時只需要roles[1],roles[2]就可以啦,角色的基本屬性設計完了后,我們接下來為每個角色開發以下幾個功能
  1. 被打中后就會掉血的功能
  2. 開槍功能
  3. 換子彈
  4. 買槍
  5. 跑、走、跳、下蹲等動作
  6. 保護人質(僅適用于警察)
  7. 不能殺同伴
  8. 。。。
我們可以把每個功能寫成一個函數,類似如下:?
?
1
2
3
4
5
6
7
8
9
10
11
def shot(by_who):
????#開了槍后要減子彈數
????pass
def got_shot(who):
????#中槍后要減血
????who[‘life_value’] -= 10
????pass
def buy_gun(who,gun_name):
????#檢查錢夠不夠,買了槍后要扣錢
????pass
...

so far so good, 繼續按照這個思路設計,再完善一下代碼,游戲的簡單版就出來了,但是在往下走之前,我們來看看上面的這種代碼寫法有沒有問題,至少從上面的代碼設計中,我看到以下幾點缺陷:
  1. 每個角色定義的屬性名稱是一樣的,但這種命名規則是我們自己約定的,從程序上來講,并沒有進行屬性合法性檢測,也就是說role 1定義的代表武器的屬性是weapon, role 2 ,3,4也是一樣的,不過如果我在新增一個角色時不小心把weapon 寫成了wepon , 這個程序本身是檢測 不到的
  2. terrorist 和police這2個角色有些功能是不同的,比如police是不能殺人質的,但是terrorist可能,隨著這個游戲開發的更復雜,我們會發現這2個角色后續有更多的不同之處, 但現在的這種寫法,我們是沒辦法 把這2個角色適用的功能區分開來的,也就是說,每個角色都可以直接調用任意功能,沒有任何限制。
  3. 我們在上面定義了got_shot()后要減血,也就是說減血這個動作是應該通過被擊中這個事件來引起的,我們調用get_shot(),got_shot()這個函數再調用每個角色里的life_value變量來減血。 但其實我不通過got_shot(),直接調用角色roles[role_id][‘life_value’] 減血也可以呀,但是如果這樣調用的話,那可以就是簡單粗暴啦,因為減血之前其它還應該判斷此角色是否穿了防彈衣等,如果穿了的話,傷害值肯定要減少,got_shot()函數里就做了這樣的檢測,你這里直接繞過的話,程序就亂了。 因此這里應該設計 成除了通過got_shot(),其它的方式是沒有辦法給角色減血的,不過在上面的程序設計里,是沒有辦法實現的。?
  4. 現在需要給所有角色添加一個可以穿防彈衣的功能,那很顯然你得在每個角色里放一個屬性來存儲此角色是否穿 了防彈衣,那就要更改每個角色的代碼,給添加一個新屬性,這樣太low了,不符合代碼可復用的原則
上面這4點問題如果不解決,以后肯定會引出更大的坑,有同學說了,解決也不復雜呀,直接在每個功能調用時做一下角色判斷啥就好了,沒錯,你要非得這么霸王硬上弓的搞也肯定是可以實現的,那你自己就開發相應的代碼來對上面提到的問題進行處理好啦。 但這些問題其實能過OOP就可以很簡單的解決。?
class Dog(object):nationality = 'jp'def __init__(self,name):     #傳參     構造方法,初始化方法self.NAME = nameself.__heart = 'normal'   #私有屬性def sayhi(self):    #類的方法(類的功能)print('hello,my name is ',self.NAME)def __del__(self):print('haha goodbye')
d =Dog('lwq')     #lwq是d的成員屬性   #先去實例化   #Dog(d,'lwq')    #實例化后產生的對象,就是實例。self其實就是實例本身
d2 = Dog('hsc')
d.sayhi()   #執行    #d.sayhi(d)
#d2.sayhi()
#print(d._Dog__heart)     #強制調用
#print(d._heart)     #不能打印,打印出錯
print(d.nationality)
d.nationality = 'CN'  #自己修改公有屬性,相當于自己在內存上創建了一個空間,就不會再調用公有的屬性。
print(d2.nationality)  #調用共有的屬性,除非自己修改后,才是找自己的

?

之前的代碼改成用OOP中的“類”來實現的話如下:?
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Role(object):
????def __init__(self,name,role,weapon,life_value=100,money=15000):
????????self.name = name
????????self.role = role
????????self.weapon = weapon
????????self.life_value = life_value
????????self.money = money
????def shot(self):
????????print("shooting...")
????def got_shot(self):
????????print("ah...,I got shot...")
????def buy_gun(self,gun_name):
????????print("just bought %s" %gun_name)
r1 = Role('Alex','police','AK47’) #生成一個角色
r2 = Role('Jack','terrorist','B22’)? #生成一個角色

先不考慮語法細節,相比靠函數拼湊出來的寫法,上面用面向對象中的類來寫最直接的改進有以下2點:
  1. 代碼量少了近一半
  2. 角色和它所具有的功能可以一目了然看出來

在真正開始分解上面代碼含義之之前,我們現來了解一些類的基本定義
類的語法
?
1
2
3
4
5
6
7
8
9
class Dog(object):
????print("hello,I am a dog!")
d = Dog() #實例化這個類,
#此時的d就是類Dog的實例化對象
#實例化,其實就是以Dog類為模版,在內存里開辟一塊空間,存上數據,賦值成一個變量名

上面的代碼其實有問題,想給狗起名字傳不進去。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog(object):
????def __init__(self,name,dog_type):
????????self.name = name
????????self.type = dog_type
????def sayhi(self):
????????print("hello,I am a dog, my name is ",self.name)
d = Dog('LiChuang',"京巴")
d.sayhi()

為什么有__init__? 為什么有self? 此時的你一臉蒙逼,相信不畫個圖,你的智商是理解不了的!  

畫圖之前, 你先注釋掉這兩句

?
1
2
3
4
# d = Dog('LiChuang', "京巴")
# d.sayhi()
print(Dog)

沒實例直接打印Dog輸出如下

?
1
<class '__main__.Dog'>

這代表什么?代表 即使不實例化,這個Dog類本身也是已經存在內存里的對不對, yes, cool,那實例化時,會產生什么化學反應呢?

?根據上圖我們得知,其實self,就是實例本身!你實例化時python會自動把這個實例本身通過self參數傳進去。

你說好吧,假裝懂了, 但下面這段代碼你又不明白了, 為何sayhi(self),要寫個self呢? 

好了,明白 了類的基本定義,接下來我們一起分解一下上面的代碼分別 是什么意思?
?
1
2
3
4
5
6
7
class Role(object): #定義一個類, class是定義類的語法,Role是類名,(object)是新式類的寫法,必須這樣寫,以后再講為什么
????def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函數,在生成一個角色時要初始化的一些屬性就填寫在這里
????????self.name = name #__init__中的第一個參數self,和這里的self都 是什么意思? 看下面解釋
????????self.role = role
????????self.weapon = weapon
????????self.life_value = life_value
????????self.money = money

上面的這個__init__()叫做初始化方法(或構造方法), 在類被調用時,這個方法(雖然它是函數形式,但在類中就不叫函數了,叫方法)會自動執行,進行一些初始化的動作,所以我們這里寫的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在創建一個角色時給它設置這些屬性,那么這第一個參數self是干毛用的呢??
初始化一個角色,就需要調用這個類一次:?
?
1
2
r1 = Role('Alex','police','AK47’) #生成一個角色 , 會自動把參數傳給Role下面的__init__(...)方法
r2 = Role('Jack','terrorist','B22’)? #生成一個角色

我們看到,上面的創建角色時,我們并沒有給__init__傳值,程序也沒未報錯,是因為,類在調用它自己的__init__(…)時自己幫你給self參數賦值了,?

?
1
2
r1 = Role('Alex','police','AK47’) #此時self 相當于 r1 ,? Role(r1,'Alex','police','AK47’)
r2 = Role('Jack','terrorist','B22’)#此時self 相當于 r2, Role(r2,'Jack','terrorist','B22’)

為什么這樣子?你拉著我說你有些猶豫,怎么會這樣子?
你執行r1?=?Role('Alex','police','AK47)時,python的解釋器其實干了兩件事:
  1. 在內存中開辟一塊空間指向r1這個變量名
  2. 調用Role這個類并執行其中的__init__(…)方法,相當于Role.__init__(r1,'Alex','police',AK47’),這么做是為什么呢? 是為了把'Alex','police',’AK47’這3個值跟剛開辟的r1關聯起來,是為了把'Alex','police',’AK47’這3個值跟剛開辟的r1關聯起來,是為了把'Alex','police',’AK47’這3個值跟剛開辟的r1關聯起來,重要的事情說3次, 因為關聯起來后,你就可以直接r1.name, r1.weapon 這樣來調用啦。所以,為實現這種關聯,在調用__init__方法時,就必須把r1這個變量也傳進去,否則__init__不知道要把那3個參數跟誰關聯呀。
  3. 明白了么哥?所以這個__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把這幾個值 存到r1的內存空間里。
如果還不明白的話,哥,去測試一下智商吧, 應該不會超過70,哈哈。
為了暴露自己的智商,此時你假裝懂了,但又問, __init__(…)我懂了,但后面的那幾個函數,噢 不對,后面那幾個方法 為什么也還需要self參數么? 不是在初始化角色的時候 ,就已經把角色的屬性跟r1綁定好了么??
good question, 先來看一下上面類中的一個buy_gun的方法:?
?
1
2
def buy_gun(self,gun_name):
????print(“%s has just bought %s” %(self.name,gun_name) )

上面這個方法通過類調用的話要寫成如下:?
?
1
2
r1 = Role('Alex','police','AK47')
r1.buy_gun("B21”) #python 會自動幫你轉成 Role.buy_gun(r1,”B21")

執行結果
#Alex has just bought B21?
依然沒給self傳值 ,但Python還是會自動的幫你把r1 賦值給self這個參數, 為什么呢? 因為,你在buy_gun(..)方法中可能要訪問r1的一些其它屬性呀, 比如這里就訪問 了r1的名字,怎么訪問呢?你得告訴這個方法呀,于是就把r1傳給了這個self參數,然后在buy_gun里調用 self.name 就相當于調用r1.name 啦,如果還想知道r1的生命值 有多少,直接寫成self.life_value就可以了。 說白了就是在調用類中的一個方法時,你得告訴人家你是誰。
好啦, 總結一下2點:
  1. 上面的這個r1?=?Role('Alex','police','AK47)動作,叫做類的“實例化”, 就是把一個虛擬的抽象的類,通過這個動作,變成了一個具體的對象了, 這個對象就叫做實例
  2. 剛才定義的這個類體現了面向對象的第一個基本特性,封裝,其實就是使用構造方法將內容封裝到某個具體對象中,然后通過對象直接或者self間接獲取被封裝的內容

面向對象的特性:

封裝

封裝最好理解了。封裝是面向對象的特征之一,是對象和類概念的主要特性。

封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。

繼承

面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展。

通過繼承創建的新類稱為“子類”或“派生類”。

被繼承的類稱為“基類”、“父類”或“超類”。

繼承的過程,就是從一般到特殊的過程。

要實現繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實現。

在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現多重繼承,可以通過多級繼承來實現。

繼承概念的實現方式主要有2類:實現繼承、接口繼承。

??????????實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;
??????????接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力(子類重構爹類方法);
在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關系應該是“屬于”關系。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因為腿并不是一個人。
抽象類僅定義將由子類創建的一般屬性和方法。

OO開發范式大致為:劃分對象→抽象類→將類組織成為層次化結構(繼承和合成) →用類與實例進行設計和實現幾個階段。

繼承示例
class SchoolMember(object):member = 0def __init__(self,name,age,sex):self.name = nameself.age = ageself.sex = sexself.enroll()def enroll(self):print('just enrolled a new school member [%s]'%self.name)SchoolMember.member +=1def tell(self):print('----%s------'%self.name)for k,v in self.__dict__.items():print('\t',k,v)def __del__(self):print('開除了[%s]....'%self.name)SchoolMember.member -=1
class School(object):"""學校類"""def open_branch(self,addr):print('openning a newbranch in ',addr)
class Teacher(SchoolMember,School):     #這里的School是多繼承"""講師類"""def __init__(self,name,age,sex,salary,course):#SchoolMember.__init__(self,name,age,sex)  #經典類寫法super(Teacher,self).__init__(name,age,sex)  #新式類寫法self.salary = salaryself.course = coursedef teaching(self):print('Teacher  [%s] is teaching [%s]'%self.name,self.course)
class Student(SchoolMember):def __init__(self,name,age,sex,course,tuition):SchoolMember.__init__(self,name,age,sex)self.course = courseself.tuition = tuitionself.amount = 0def pay_tuition(self,amount):print('student [%s] has just paied [%s]'%(self.name,self.amount))self.amount +=amount
t1 = Teacher('wusir',28,'M',3000,'python')
s1 = Student('lwq',12,'M','python',11000)
s2 = Student('hsc',11,'M','python',11000)
t1.tell()
s1.tell()
del t1

?

關聯

class F1:def __init__(self,n):self.N = nprint('F1')
class F2:def __init__(self,arg1):self.a = arg1print('F2')
class F3:def __init__(self,arg2):self.b = arg2print('F3')
o1 = F1('alex')
o2 = F2(o1)
o3 = F3(o2)#####輸出alex
o3.b.a.N


繼承

class F1:def __init__(self):print('F1')def a1(self):print('F1a1')def a2(self):print('F1a2')
class F2(F1):def __init__(self):print('F2')def a1(self):self.a2()print('F2a1')def a2(self):print('F2a2')
class F3(F2):def __init__(self):print('F3')def a2(self):print('F3a2')
obj = F3()
obj.a1()

?

?

class SchoolMember(object):member = 0def __init__(self,name,age,sex):self.name = nameself.age = ageself.sex = sexself.enroll()def enroll(self):print('just enrolled a new school member [%s]'%self.name)SchoolMember.member +=1def tell(self):print('----%s------'%self.name)for k,v in self.__dict__.items():print('\t',k,v)def __del__(self):print('開除了[%s]....'%self.name)SchoolMember.member -=1
class School(object):"""學校類"""def open_branch(self,addr):print('openning a newbranch in ',addr)
class Teacher(SchoolMember,School):     #這里的School是多繼承"""講師類"""def __init__(self,name,age,sex,salary,course):#SchoolMember.__init__(self,name,age,sex)  #經典類寫法super(Teacher,self).__init__(name,age,sex)  #新式類寫法self.salary = salaryself.course = coursedef teaching(self):print('Teacher  [%s] is teaching [%s]'%self.name,self.course)
class Student(SchoolMember):def __init__(self,name,age,sex,course,tuition):SchoolMember.__init__(self,name,age,sex)self.course = courseself.tuition = tuitionself.amount = 0def pay_tuition(self,amount):print('student [%s] has just paied [%s]'%(self.name,self.amount))self.amount +=amount
t1 = Teacher('wusir',28,'M',3000,'python')
s1 = Student('lwq',12,'M','python',11000)
s2 = Student('hsc',11,'M','python',11000)
t1.tell()
s1.tell()
del t1


小結

面向對象:(更容易擴展,提高代碼使用效率,使代碼更強,更清晰,更適合復雜項目的開發) 類 對象 
封裝: 1.防止數據被隨意修改 2.使外部程序不需要關注對象內部的構造,只需要通過此對象對外提供的接口進行直接訪問即可。
繼承: 通過父類---〉子類的方式以最小代碼量的方式實現不同角色的共同點和不同點同時存在。 多態: 接口的繼承。

類的基本定義: 類==》模版 類----〉實例化----〉實例 __init__ 構造函數 self.name = name 屬性(成員變量)或者字段 def sayhi() 方法或者動態屬性
封裝: 成員屬性:==》實例變量
公有屬性:在類里直接定義的屬性,既是公有屬性,存在類的內存里,所有實例共享。
私有屬性:__private_attr_name = value #兩個下劃線加名字 對外不提供只讀訪問接口 def get_heart(self): return self._heart 強制訪問: r1._role__heart 類前加一個下劃線,屬性前加兩個下劃線。
del 析構函數(銷毀實例時運行) 關閉連接。
繼承
靜態方法;沒有__init__(可以有任意個參數),直接通過類調用方法,保存在類中,調用者是類, class F1: @staticmethod def a1(): print('alex') F1.a1()
?

轉載于:https://www.cnblogs.com/l-w-q/p/6082103.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371957.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371957.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371957.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

西安郵電大學計算機科學與技術有專碩嗎,2020年西安郵電大學計算機學院考研擬錄取名單及排名!...

20考研復試調劑群&#xff1a;4197552812020年西安郵電大學計算機學院碩士研究生招生復試成績及綜合排名各位考生&#xff1a;現將我院2020年碩士研究生招生復試成績及綜合排名公布(最終錄取名單及新生學籍注冊均以“全國碩士研究生招生信息公開平臺”備案信息為準)&#xff0c…

用Java排序的五種有用方法

Java排序快速概述&#xff1a; 正常的列表&#xff1a; private static List VEGETABLES Arrays.asList("apple", "cocumbers", "blackberry");Collections.sort(VEGETABLES);output: apple, blackberry, cocumbers反向排序&#xff1a; pri…

[python]-數據科學庫Numpy學習

一、Numpy簡介&#xff1a; Python中用列表(list)保存一組值&#xff0c;可以用來當作數組使用&#xff0c;不過由于列表的元素可以是任何對象&#xff0c;因此列表中所保存的是對象的指針。這樣為了保存一個簡單的[1,2,3]&#xff0c;需要有3個指針和三個整數對象。對于數值運…

檢測一個點, 是否在一個半圓之內的方法

demo: http://jsbin.com/lihiwigaso 需求: 一個圓分成分部分, 鼠標滑上不同的區域顯示不同的顏色 思路: 先判斷這個點是否在圓之內, 再判斷是否在所在的三角形之內就可以了 所需要的全部源碼: <!DOCTYPE html> <html> <head><meta charset"utf-8&quo…

計算機網絡設備接地規范,網絡機房防雷接地的四種方式及靜電要求

編輯----河南新時代防雷由于計算機網絡系統的核心設備都放置在網絡機房內&#xff0c;因而網絡機房防雷接地有了較高的環境要求&#xff0c;良好的接地系統是保證機房計算機及網絡設備安全運行&#xff0c;以及工作人員人身安全的重要措施。直流地的接法通常采用網格地&#xf…

抓住尾部的StackOverFlowError

使用Java程序時可能要處理的一種更煩人的情況是StackOverFlowError&#xff0c;如果您有一個很好的可生產的測試用例&#xff0c;那么關于使用堆棧大小或設置條件斷點/某種痕跡 。 但是&#xff0c;如果您有一個測試案例可能一次失敗100次&#xff0c;或者像我的案例一樣在AWTM…

Gunicorn配置部分的翻譯

寫在前面&#xff0c;雖然翻譯得很爛&#xff0c;但也是我的勞動成果&#xff0c;轉載請注明出處&#xff0c;謝謝。 Gunicorn版本號19.7.1 Gunicorn配置 概述 三種配置方式 優先級如下&#xff0c;越后的優先級越大 1.框架的設置&#xff08;現在只有paster在用這個&#xff0…

臺式計算機風扇聲音大怎么處理,如何解決電腦電源風扇聲音大的問題?

現在的臺式機一般用3到5年后&#xff0c;一些問題自然也就慢慢表現出來了。很多網友在使用電腦過程中都有電腦風扇聲音大怎么辦的問題&#xff0c;電腦風扇聲音大就會讓人覺得使用電腦很不舒服&#xff0c;怎么辦好呢&#xff1f;出現重要的問題要如何解決好呢&#xff1f;現在…

jsp分頁功能

http://blog.csdn.net/xiazdong/article/details/6857515轉載于:https://www.cnblogs.com/Baronboy/p/6112403.html

Spring Security第1部分–具有數據庫的簡單登錄應用程序

什么是Spring Security&#xff1f; Spring Security是一個提供安全解決方案的框架&#xff0c;可在Web請求級別和方法級別上處理身份驗證和授權。 Spring安全性通過兩種方式處理安全性。 一種是安全的Web請求&#xff0c;另一種是在URL級別限制訪問。 Spring Security使用Serv…

計算機應用 winxp,2017年職稱計算機考試模塊WindowsXP試題

2017年職稱計算機考試模塊WindowsXP試題全國專業技術人員計算機應用能力考試是專業技術人員資格考試的一種。接下來應屆畢業生小編為大家搜索整理了2017年職稱計算機考試模塊WindowsXP試題&#xff0c;希望大家有所幫助。1. Windows XP中刪除某個文件的快捷方式【 A 】。A. 對原…

Python基礎(8)_迭代器、生成器、列表解析

一、迭代器 1、什么是迭代 1 重復   2 下次重復一定是基于上一次的結果而來 1 l[1,2,3,4] 2 count0 3 while count < len(l): 4 print(l[count]) 5 count1 迭代舉例2、可迭代對象 可進行.__iter__()操作的為可迭代對象 #print(isinstance(str1,Iterable)),判斷str…

Angularjs2-EXCEPTION: Response with status: 200 Ok for URL:

利用jsonp跨域請求數居&#xff0c;報錯 core.umd.js:3070 EXCEPTION: Response with status: 200 Ok for URL: 參考&#xff1a;stackoverflow 未解決。。。腦仁疼。。。有小伙伴也碰到過這個問題么&#xff1f; 16/11/30 問題解決 1.服務器端API允許跨域訪問(返回的數據添加允…

圖片無法刪除要計算機管理員,存在桌面的圖片刪不掉,怎么處理?提示是需要管理員權限。...

將下面代碼復制到記事本里&#xff0c;重命名為1.bat&#xff0c;然后打開&#xff0c;這時你右鍵圖片會出現管理員取得所有權&#xff0c;然后取得所有權后再刪除試試Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\runas]管理員取得所有權NoWorkingDirecto…

Java對象序列化的本機C / C ++類似性能

您是否曾經希望過像使用C 這樣的本地語言將Java對象轉換成字節流一樣快的速度&#xff1f; 如果您使用標準的Java序列化&#xff0c;您可能會對性能感到失望。 Java序列化的目的是與盡可能快而緊湊地序列化對象的目的截然不同。 為什么我們需要快速緊湊的序列化&#xff1f; 我…

WebStrom Sass 編譯配置 windows

第一步&#xff1a; 先安裝Ruby下載 一路next 安裝完成后打開開始菜單 打開后輸入 gem install sass sass -v 出現版本號說明成功 第二部配置webstorm 在webstorm中settings中搜索file watchers工具&#xff0c;在此工具中添加一個scss的watcher 確定&#xff0c;打開一個scss…

非本地跳轉之setjmp與longjmp

非本地跳轉(unlocal jump)是與本地跳轉相對應的一個概念。 本地跳轉主要指的是類似于goto語句的一系列應用&#xff0c;當設置了標志之后&#xff0c;可以跳到所在函數內部的標號上。然而&#xff0c;本地跳轉不能將控制權轉移到所在程序的任意地點&#xff0c;不能跨越函數&am…

清華計算機自主招生試題,2017年清華大學自主招生筆試題

2017年清華大學自主招生筆試題2017高考結束后&#xff0c;全國各大高校自主招生面試開始了&#xff0c;以下是百分網小編搜索整理的關于2017年清華大學自主招生筆試題&#xff0c;供各位參考&#xff0c;希望對大家有所幫助!想了解更多相關信息請持續關注我們應屆畢業生考試網!…

擴展劑:模式還是反模式?

擴展器模式在最近幾年變得很流行&#xff0c;甚至已經在OSGi標準&#xff08;例如&#xff0c;藍圖服務和Web應用程序規范&#xff09;中使用。 在處女座&#xff0c;我們從一開始就與擴展程序一起工作&#xff0c;但是盡管它們具有優勢&#xff0c;但它們仍有一些明顯的缺點。…

python html格式編碼

web應用如用到ace等網絡編輯器的時候&#xff0c;如要支持html,xml等格式的文件編輯&#xff0c;輸入ace 的文本內容必須先進行html格式編碼&#xff1a; def html_escape(content): import cgi return cgi.escape(content)轉載于:https://www.cnblogs.com/zhouxiaoming/p/703…