Python學習筆記(七)——面向對象

Python面向對象

 概述php

  • 面向過程:根據業務邏輯從上到下寫壘代碼
  • 函數式:將某功能代碼封裝到函數中,往後便無需重複編寫,僅調用函數便可
  • 面向對象:對函數進行分類和封裝,讓開發「更快更好更強...」

面向過程編程最易被初學者接受,其每每用一長段代碼來實現指定功能,開發過程當中最多見的操做就是粘貼複製,即:將以前實現的代碼塊複製到現需功能處,此爲1.0。html

隨着時間的推移,開始使用了函數式編程,加強代碼的重用性和可讀性,此爲2.0。java

而從如今開始,咱們即未來學習編程方式的升級版——面向對象編程(Object Oriented Programming, OOP, 面向對象程序設計),華麗麗的3.0。python

PS:Java和C#來講只支持面向對象編程,而python比較靈活即支持面向對象編程也支持函數式編程。程序員

 

建立類和對象面試

 面向對象編程是一種編程方式,此編程方式須要使用 「類」 和 「對象」 來實現,因此,面向對象編程其實就是對 「類」 和 「對象」 的使用。數據庫

  • 類就是一個模板,模板裏能夠包含多個函數,函數裏實現一些功能;
  • 對象則是根據模板建立的實例,經過實例對象能夠執行類中的函數;

  • class是關鍵字,表示類
  • 建立對象,類名稱後加括號便可

ps:類中的函數第一個參數必須是self(詳細見:類的三大特性之封裝)
  類中定義的函數叫作 「方法」。編程

1
2
3
4
5
6
7
8
9
10
11
12
# 建立類
class  foo:
     def  bar( self ):
         print  "bar"
 
     def  hello( self ,name):
         print  "I am %s"  %  name
 
# 根據類foo建立對象obj
obj  =  foo()
obj.bar()   # 執行bar方法
obj.hello( 'korala' )   # 執行hello方法

運行結果:c#

1
2
bar
I am korala

看了上面的例子是否是有點困惑啊?使用函數式編程和麪向對象編程方式來執行一個「方法」時函數要比面向對象簡便。ruby

  • 面向對象:【建立對象】【經過對象執行方法】
  • 函數編程:【執行函數】

看了上述的對比函數的確要比面向對象簡便,然而事情的真相是。。。不一樣的場景中其適合的編程方式也是不同滴。 

總結:函數式的應用場景 --> 各個函數之間是獨立且無共用的數據。

至於面向對象該如何應用呢?別急,接着往下看。

 

面向對象的三大特性

想知道面向對象應用在哪些場景,先來了解它的三大特性:

  • 封裝
  • 繼承
  • 多態

1、  封裝

封裝,顧名思義就是將內容封裝到某個地方,之後再去調用被封裝在某處的內容。

因此,在使用面向對象的封裝特性時,須要:

  • 將內容封裝到某處
  • 從某處調用被封裝的內容

第一步:將內容封裝到某處

1
2
3
4
5
6
7
8
9
10
11
12
13
# 建立類
class  foo:
     def  __int__( self , name, age):   # 稱爲構造方法,根據類建立對象時自動執行
         self .name  =  name
         self .age  =  age
 
# 根據類foo建立對象
# 自動執行foo類的 __init__ 方法
obj1  =  foo( 'wxy' 20 )   # 將wxy和20分別封裝到 obj1 self 的name和age屬性中
 
# 根據類foo建立對象
# 自動執行foo類的 __init__ 方法
obj2  =  foo( 'korala' 18 )   # 將korala和18分別封裝到 obj2 self 的name和age屬性中

self 是一個形式參數,當執行 obj1 = Foo('wxy',20) 時,self 等於 obj1;當執行 obj2 = Foo('korala', 18 ) 時,self 等於 obj2。

因此,內容其實被封裝到了對象 obj1 和 obj2 中,每一個對象中都有 name 和 age 屬性,在內存裏相似於下圖來保存。

第二步:從某處調用被封裝的內容

調用被封裝的內容時,有兩種狀況:

  • 經過對象直接調用
  • 經過self間接調用

一、經過對象直接調用被封裝的內容

上圖展現了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式能夠如此調用被封裝的內容:對象.屬性名

1
2
3
4
5
6
7
8
9
10
11
12
class  foo:
     def  __int__( self , name, age):
         self .name  =  name
         self .age  =  age
 
obj1  =  foo( 'wxy' 20 )
print  obj1.name   # 直接調用obj1對象的name屬性
print  obj1.age   # 直接調用obj1對象的age屬性
 
obj2  =  foo( 'korala' 18 )
print  obj2.name   # 直接調用obj2對象的name屬性
print  obj2.age   # 直接調用obj2對象的age屬性

二、經過self間接調用被封裝的內容

執行類中的方法時,須要經過self間接調用被封裝的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  foo:
     def  __int__( self , name, age):
         self .name  =  name
         self .age  =  age
 
     def  detail( self ):
         print  self .name
         print  self .age
 
obj1  =  foo( 'wxy' 20 )
obj1.detail()   # python默認會將obj1傳給self參數,即:obj1.detail(obj1),故此時方法內部的 self.name 是 wxy; self.age 是 20
 
obj2  =  foo( 'korala' 18 )
obj2.detail()   # python默認會將obj2傳給self參數,即:obj2.detail(obj2),故此時方法內部的 self.name 是 korala ; self.age 是 18

綜上所述,對於面向對象的封裝來講,其實就是使用構造方法將內容封裝到 對象 中,而後經過對象直接或者self間接獲取被封裝的內容。

 

練習一:在終端輸出以下信息
小明,10歲,男,上山去砍柴
小明,10歲,男,開車去東北
小明,10歲,男,最愛大保健
老李,90歲,男,上山去砍柴
老李,90歲,男,開車去東北
老李,90歲,男,最愛大保健
# 函數式編程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def  kanchai(name, age, gender):
     print  "%s,%s歲,%s,上山去砍柴"  %  (name, age, gender)
 
def  qudongbei(name, age, gender):
     print  "%s,%s歲,%s,開車去東北"  %  (name, age, gender)
 
def  dabaojian(name, age, gender):
     print  "%s,%s歲,%s,最愛大保健"  %  (name, age, gender)
 
kanchai( '小明' 10 '男' )
qudongbei( '小明' 10 '男' )
dabaojian( '小明' 10 '男' )
 
kanchai( '老李' 90 '男' )
qudongbei( '老李' 90 '男' )
dabaojian( '老李' 90 '男' )

# 面向對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class  Foo:
     def  __init__( self , name, age ,gender):
         self .name  =  name
         self .age  =  age
         self .gender  =  gender
 
     def  kanchai( self ):
         print  "%s,%s歲,%s,上山去砍柴"  % ( self .name,  self .age,  self .gender)
 
     def  qudongbei( self ):
         print  "%s,%s歲,%s,開車去東北"  % ( self .name,  self .age,  self .gender)
 
     def  dabaojian( self ):
         print  "%s,%s歲,%s,最愛大保健"  % ( self .name,  self .age,  self .gender)
 
xiaoming  =  Foo( '小明' 10 '男' )
xiaoming.kanchai()
xiaoming.qudongbei()
xiaoming.dabaojian()
 
laoli  =  Foo( '老李' 90 '男' )
laoli.kanchai()
laoli.qudongbei()
laoli.dabaojian()

上述對比能夠看出,若是使用函數式編程,須要在每次執行函數時傳入相同的參數,若是參數多的話,就須要n次粘貼複製了;而對於面向對象只須要在建立對象時,將全部須要的參數封裝到當前對象中,以後再次使用時,經過self間接去當前對象中取值便可。

 

練習二:遊戲人生程序
一、建立三個遊戲人物,分別是:
蒼井井,女,18,初始戰鬥力1000
東尼木木,男,20,初始戰鬥力1800
波多多,女,19,初始戰鬥力2500


二、遊戲場景,分別:
草叢戰鬥,消耗200戰鬥力
自我修煉,增加100戰鬥力
多人遊戲,消耗500戰鬥力

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# #####################  定義實現功能的類  #####################
 
class  Person:
     def  __init__( self , na, gen, age, fig):
         self .name  =  na
         self .gender  =  gen
         self .age  =  age
         self .fight  = fig
 
     def  grassland( self ):
         """註釋:草叢戰鬥,消耗200戰鬥力"""
 
         self .fight  =  self .fight  -  200
 
     def  practice( self ):
         """註釋:自我修煉,增加100戰鬥力"""
 
         self .fight  =  self .fight  +  200
 
     def  incest( self ):
         """註釋:多人遊戲,消耗500戰鬥力"""
 
         self .fight  =  self .fight  -  500
 
     def  detail( self ):
         """註釋:當前對象的詳細狀況"""
 
         temp  =  "姓名:%s ; 性別:%s ; 年齡:%s ; 戰鬥力:%s"   %  ( self .name,  self .gender,  self .age,  self .fight)
         print  temp
 
 
# #####################  開始遊戲  #####################
 
cang  =  Person( '蒼井井' '女' 18 1000 )     # 建立蒼井井角色
dong  =  Person( '東尼木木' '男' 20 1800 )   # 建立東尼木木角色
bo  =  Person( '波多多' '女' 19 2500 )       # 建立波多多角色
 
cang.incest()  #蒼井空參加一次多人遊戲
dong.practice() #東尼木木自我修煉了一次
bo.grassland()  #波多多參加一次草叢戰鬥
 
 
#輸出當前全部人的詳細狀況
cang.detail()
dong.detail()
bo.detail()
 
 
cang.incest()  #蒼井空又參加一次多人遊戲
dong.incest()  #東尼木木也參加了一個多人遊戲
bo.practice()  #波多多自我修煉了一次
 
#輸出當前全部人的詳細狀況
cang.detail()
dong.detail()
bo.detail()

小結:

封裝:一、將數據進行封裝,便於使用時取出數據;

  二、根據一個模板建立使用對象(相同屬性);

執行過程:
類對象指針 ——> 經過指針找到類,類中找方法,方法執行(對象中隱含着類中的指針 ==> 關聯)

 

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
28
29
30
31
class  貓:
     def  喵喵叫( self ):
         print  '喵喵叫'
 
     def  吃( self ):
         # do something
 
     def  喝( self ):
         # do something
 
     def  拉( self ):
         # do something
 
     def  撒( self ):
         # do something
 
class  狗:
     def  汪汪叫( self ):
         print  '喵喵叫'
 
     def  吃( self ):
         # do something
 
     def  喝( self ):
         # do something
 
     def  拉( self ):
         # do something
 
     def  撒( self ):
         # do something

上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,而咱們卻分別的貓和狗的類中編寫了兩次。若是使用 繼承 的思想,以下實現:

  動物:吃、喝、拉、撒

     貓:喵喵叫(貓繼承動物的功能)

     狗:汪汪叫(狗繼承動物的功能)

# 僞代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class  動物:
     def  吃( self ):
         # do something
 
     def  喝( self ):
         # do something
 
     def  拉( self ):
         # do something
 
     def  撒( self ):
         # do something
 
# 在類後面括號中寫入另一個類名,表示當前類繼承另一個類
class  貓(動物):
     def  喵喵叫( self ):
         print  '喵喵叫'
         
# 在類後面括號中寫入另一個類名,表示當前類繼承另一個類
class  狗(動物):
 
     def  汪汪叫( self ):
         print  '喵喵叫'

# 代碼實例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
class  Animal:
     def  eat( self ):
         print  "%s 吃 "  % self .name
 
     def  drink( self ):
         print  "%s 喝 "  % self .name
 
     def  shit( self ):
         print  "%s 拉 "  % self .name
 
     def  pee( self ):
         print  "%s 撒 "  % self .name
 
 
class  Cat(Animal):
     def  __init__( self , name):
         self .name  =  name
         self .breed  =  '貓'
 
     def  cry( self ):
         print  '喵喵叫'
 
class  Dog(Animal):
 
     def  __init__( self , name):
         self .name  =  name
         self .breed  =  '狗'
 
     def  cry( self ):
         print  '汪汪叫'
 
 
# ######### 執行 #########
 
c1  =  Cat( '小白家的小黑貓' )
c1.eat()
 
c2  =  Cat( '小黑的小白貓' )
c2.drink()
 
d1  =  Dog( '胖子家的小瘦狗' )
d1.eat()
因此,對於面向對象的繼承來講,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而沒必要一一實現每一個方法。
PS:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不一樣而已。

學習了繼承的寫法以後,咱們用代碼來是上述阿貓阿狗的功能:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class  Animal:
     def  eat( self ):
         print  "%s 吃 "  % self .name
 
     def  drink( self ):
         print  "%s 喝 "  % self .name
 
     def  shit( self ):
         print  "%s 拉 "  % self .name
 
     def  pee( self ):
         print  "%s 撒 "  % self .name
 
 
class  Cat(Animal):
 
     def  __init__( self , name):
         self .name  =  name
         self .breed =  '貓'
 
     def  cry( self ):
         print  '喵喵叫'
 
class  Dog(Animal):
     
     def  __init__( self , name):
         self .name  =  name
         self .breed =  '狗'
         
     def  cry( self ):
         print  '汪汪叫'
         
 
# ######### 執行 #########
 
c1  =  Cat( '小白家的小黑貓' )
c1.eat()
 
c2  =  Cat( '小黑的小白貓' )
c2.drink()
 
d1  =  Dog( '胖子家的小瘦狗' )
d1.eat()

 那麼問題又來了,多繼承呢?

  • 是否能夠繼承多個類
  • 若是繼承的多個類每一個類中都定了相同的函數,那麼那一個會被使用呢?

一、Python的類能夠繼承多個類,Java和C#中則只能繼承一個類

二、Python的類若是繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先 和 廣度優先

  • 當類是經典類時,多繼承狀況下,會按照深度優先方式查找
  • 當類是新式類時,多繼承狀況下,會按照廣度優先方式查找

經典類和新式類,從字面上能夠看出一個老一個新,新的必然包含了跟多的功能,也是以後推薦的寫法,從寫法上區分的話,若是 當前類或者父類繼承了object類,那麼該類即是新式類,不然即是經典類。

 # 經典類多繼承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  D:
     def  bar( self ):
         print  'D.bar'
 
class  C(D):
     def  bar( self ):
         print  'C.bar'
 
class  B(D):
     def  bar( self ):
         print  'B.bar'
 
class  A(B, C):
     def  bar( self ):
         print  'A.bar'
         
=  A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> D --> C
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()

# 新式類多繼承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  D( object ):
     def  bar( self ):
         print  'D.bar'
 
class  C(D):
     def  bar( self ):
         print  'C.bar'
 
class  B(D):
     def  bar( self ):
         print  'B.bar'
 
class  A(B, C):
     def  bar( self ):
         print  'A.bar'
 
=  A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()

經典類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯。

新式類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯。

注意:在上述查找過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了。

小結:

繼承:只跟類有關係;
父類、子類
基類、派生類

函數式 ==> 函數式面向對象(低級)經典類 ==> 函數式面向對象(添加新功能)新式類(直接或間接繼承)
java,c#:只能繼承一個類,不能繼承多個類
python,ruby,php:多繼承支持——>
            廣度(橫向)優先(新式類):離誰越近就先找誰
            深度(縱向)優先(經典類)
一個類D繼承了多個類A,B,C,對於新式類來講,對於類中的同一功能,尋找順序爲:C,B,A
深度優先則順序爲:A,B,C
 
3、多態
Pyhon不支持多態而且也用不到多態,多態的概念是應用於Java和C#這一類強類型語言中,而Python崇尚「鴨子類型」。

# python僞代碼實現java或c#的多態

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
class  F1:
     pass
 
class  S1(F1):
     def  show( self ):
         print  'S1.show'
 
class  S2(F1):
     def  show( self ):
         print  'S2.show'
 
# 因爲在Java或C#中定義函數參數時,必須指定參數的類型
# 爲了讓Func函數既能夠執行S1對象的show方法,又能夠執行S2對象的show方法,因此,定義了一個S1和S2類的父類
# 而實際傳入的參數是:S1對象和S2對象
 
def  Func(F1 obj):
     """Func函數須要接收一個F1類型或者F1子類的類型"""
 
     print  obj.show()
 
s1_obj  =  S1()
Func(s1_obj)  # 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show
 
s2_obj  =  S2()
Func(s2_obj)  # 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show

# python 「鴨子類型」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  F1:
     pass
 
class  S1(F1):
     def  show( self ):
         print  'S1.show'
 
class  S2(F1):
     def  show( self ):
         print  'S2.show'
 
def  Func(obj):
     print  obj.show()
 
s1_obj  =  S1()
Func(s1_obj)
 
s2_obj  =  S2()
Func(s2_obj)

 基礎知識總結:

  • 面向對象是一種編程方式,此編程方式的實現是基於對  和 對象 的使用
  • 類 是一個模板,模板中包裝了多個「函數」供使用
  • 對象,根據模板建立的實例(即:對象),實例用於調用被包裝在類中的函數
  • 面向對象三大特性:封裝、繼承和多態 

問答專區

問題一:什麼樣的代碼纔是面向對象?

答:從簡單來講,若是程序中的全部功能都是用 類 和 對象 來實現,那麼就是面向對象編程了。

問題二:函數式編程 和 面向對象 如何選擇?分別在什麼狀況下使用?

答:須知:對於 C# 和 Java 程序員來講不存在這個問題,由於該兩門語言只支持面向對象編程(不支持函數式編程)。而對於 Python 和 PHP 等語言卻同時支持兩種編程方式,且函數式編程能完成的操做,面向對象均可以實現;而面向對象的能完成的操做,函數式編程不行(函數式編程沒法實現面向對象的封裝功能)。

因此,通常在Python開發中,所有使用面向對象 或 面向對象和函數式混合使用。

面向對象的應用場景:

1. 多函數需使用共同的值,如:數據庫的增、刪、改、查操做都須要鏈接數據庫字符串、主機名、用戶名和密碼。

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
class  SqlHelper:
     def  __init__( self , host, user, pwd):
 
         self .host  =  host
         self .user  =  user
         self .pwd  =  pwd
 
     def  增( self ):
         # 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數據庫鏈接
         # do something
         # 關閉數據庫鏈接
 
     def  刪( self ):
         # 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數據庫鏈接
         # do something
         # 關閉數據庫鏈接
 
     def  改( self ):
         # 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數據庫鏈接
         # do something
         # 關閉數據庫鏈接
 
     def  查( self ):
     # 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數據庫鏈接
         # do something
         # 關閉數據庫鏈接# do something

2. 須要建立多個事物,每一個事物屬性個數相同,可是值的需求
如:張3、李4、楊五,他們都有姓名、年齡、血型,但其都是不相同。即:屬性個數相同,但值不相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
class  Person:
     def  __init__( self , name ,age ,blood_type):
         self .name  =  name
         self .age  =  age
         self .blood_type  =  blood_type
 
     def  detail( self ):
         temp  =  "i am %s, age %s , blood type %s "  %  ( self .name,  self .age,  self .blood_type)
         print  temp
 
zhangsan  =  Person( '張三' 18 'A' )
lisi  =  Person( '李四' 73 'AB' )
yangwu  =  Person( '楊五' 84 'A' )

問題三:類和對象在內存中是如何保存?

答:類以及類中的方法在內存中只有一份,而根據類建立的每個對象都在內存中須要存一份,大體以下圖:

如上圖所示,根據類建立對象時,對象中除了封裝 name 和 age 的值以外,還會保存一個類對象指針,該值指向當前對象的類。

當經過 obj1 執行 【方法一】 時,過程以下:

1. 根據當前對象中的 類對象指針 找到類中的方法

2. 將對象 obj1 看成參數傳給 方法的第一個參數 self 

 

進階篇

類的成員

類的成員能夠分爲三大類:字段、方法和屬性。

PS全部成員中,只有普通字段的內容保存對象中,即:根據此類建立了多少對象,在內存中就有多少個普通字段。而其餘的成員,則都是保存在類中,即:不管對象的多少,在內存中只建立一份。
1、字段

字段包括:普通字段和靜態字段,他們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不一樣,

  • 普通字段屬於對象
  • 靜態字段屬於
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 字段的定義和使用
class  Province:
     # 靜態字段
     country  =  '中國'
 
     def  __init__( self , name):
         # 普通字段
         self .name  =  name
 
# 直接訪問普通字段
obj  =  Province( '河北省' )
print  obj.name
 
# 直接訪問靜態字段
Province.country
由上述代碼能夠看出【普通字段須要經過對象來訪問】【靜態字段經過類訪問】,在使用上能夠看出普通字段和靜態字段的歸屬是不一樣的。其在內容的存儲方式相似以下圖:


由上圖可知:

  • 靜態字段在內存中只保存一份
  • 普通字段在每一個對象中都要保存一份

應用場景: 經過類建立對象時,若是每一個對象都具備相同的字段,那麼就使用靜態字段

PS普通字段——保存在對象中

  靜態字段——保存在類中

 

2、方法

方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣。

  • 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  • 類方法:由調用; 至少一個cls參數;執行類方法時,自動將調用該方法的複製給cls
  • 靜態方法:由調用;無默認參數;
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
# 方法的定義和使用
class  Foo:
     def  __init__( self , name):
         self .name  =  name
 
     def  ord_func( self ):
         """ 定義普通方法,至少有一個self參數 """
         # print self.name
         print  '普通方法'
 
     @classmethod
     def  class_func( cls ):
         """ 定義類方法,至少有一個cls參數 """
         print  '類方法'
 
     @staticmethod
     def  static_func():
         """ 定義靜態方法 ,無默認參數"""
         print  '靜態方法'
 
# 調用普通方法
=  Foo()
f.ord_func()
 
# 調用類方法
Foo.class_func()
 
# 調用靜態方法
Foo.static_func()

相同點:對於全部的方法而言,均屬於類(非對象)中,因此,在內存中也只保存一份。

不一樣點:方法調用者不一樣、調用方法時自動傳入的參數不一樣。

 

PS:普通方法——對象觸發,至少一個self參數
     類方法——類調用,只能一個cls參數 ==> 無需建立對象
     靜態方法——類調用,無默認參數 ==> 類+靜態方法=函數(節省內存),不用單首創建內存

 

3、屬性

若是你已經瞭解Python類中的方法,那麼屬性就很是簡單了,由於Python中的屬性實際上是普通方法的變種。

對於屬性,有如下三個知識點:

  • 屬性的基本使用
  • 屬性的兩種定義方式
一、   屬性的基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 屬性的定義和使用
# ############### 定義 ###############
class  Foo:
     def  func( self ):
         pass
 
     # 定義屬性
     @property
     def  prop( self ):
         pass
 
# ############### 調用 ###############
foo_obj  =  Foo()
 
foo_obj.func()
foo_obj.prop    #調用屬性

由屬性的定義和調用要注意一下幾點:

  • 定義時,在普通方法的基礎上添加 @property 裝飾器;
  • 定義時,屬性僅有一個self參數
  • 調用時,無需括號
               方法:foo_obj.func()
               屬性:foo_obj.prop

注意:屬性存在乎義是:訪問屬性時能夠製造出和訪問字段徹底相同的假象。

     屬性由方法變種而來,若是Python中沒有屬性,方法徹底能夠代替其功能。

實例:對於主機列表頁面,每次請求不可能把數據庫中的全部內容都顯示到頁面上,而是經過分頁的功能局部顯示,因此在向數據庫中請求數據時就要顯示的指定獲取從第m條到第n條的全部數據(即:limit m,n),這個分頁的功能包括:

    • 根據用戶請求的當前頁和總數據條數計算出 m 和 n
    • 根據m 和 n 去數據庫中請求數據 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ############### 定義 ###############
class  Pager:
     def  __init__( self , current_page):
         # 用戶當前請求的頁碼(第一頁、第二頁...)
         self .current_page  =  current_page
         # 每頁默認顯示10條數據
         self .per_items  =  10
 
     @property
     def  start( self ):
         val  =  ( self .current_page  -  1 *  self .per_items
         return  val
 
     @property
     def  end( self ):
         val  =  self .current_page  *  self .per_items
         return  val
 
# ############### 調用 ###############
=  Pager( 1 )
p.start 就是起始值,即:m
p.end   就是結束值,即:n
從上述可見,Python的屬性的功能是:屬性內部進行一系列的邏輯計算,最終將計算結果返回。

2、屬性的兩種定義方式

屬性的定義有兩種方式:

  • 裝飾器 即:在方法上應用裝飾器
  • 靜態字段 即:在類中定義值爲property對象的靜態字段

裝飾器方式:在類的普通方法上應用@property裝飾器

咱們知道Python中的類有經典類和新式類,新式類的屬性比經典類的屬性豐富。( 若是類繼object,那麼該類是新式類 )
經典類,具備一種@property裝飾器(如上一步實例)

1
2
3
4
5
6
7
8
# ############### 定義 ###############   
class  Goods:
     @property
     def  price( self ):
         return  "wupeiqi"
# ############### 調用 ###############
obj  =  Goods()
result  =  obj.price   # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
新式類,具備三種@property裝飾器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ############### 定義 ###############
class  Goods( object ):
     @property
     def  price( self ):
         print  '@property'
 
     @price .setter
     def  price( self , value):
         print  '@price.setter'
 
     @price .deleter
     def  price( self ):
         print  '@price.deleter'
# ############### 調用 ###############
obj  =  Goods()
obj.price           # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
obj.price  =  123     # 自動執行 @price.setter 修飾的 price 方法,並將  123 賦值給方法的參數
del  obj.price       # 自動執行 @price.deleter 修飾的 price 方法

注:經典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
    新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法

因爲新式類中具備三種訪問方式,咱們能夠根據他們幾個屬性的訪問特色,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除

# 實例
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
class  Goods( object ):
     def  __init__( self ):
         # 原價
         self .original_price  =  100
         # 折扣
         self .discount  =  0.8
 
     @property
     def  price( self ):
         # 實際價格 = 原價 * 折扣
         new_price  =  self .original_price  *  self .discount
         return  new_price
 
     @price .setter
     def  price( self , value):
         self .original_price  =  value
 
     @price .deltter
     def  price( self , value):
         del  self .original_price
 
obj  =  Goods()
obj.price          # 獲取商品價格
obj.price  =  200    # 修改商品原價
del  obj.price      # 刪除商品原價

靜態字段方式,建立值爲property對象的靜態字段

當使用靜態字段的方式建立屬性時,經典類和新式類無區別

1
2
3
4
5
6
7
8
9
class  Foo:
     def  get_bar( self ):
         return  'wupeiqi'
 
     BAR  =  property (get_bar)
 
obj  =  Foo()
reuslt  =  obj.BAR         # 自動調用get_bar方法,並獲取方法的返回值
print  reuslt

property的構造方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.__doc__ ,此參數是該屬性的描述信息
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  Foo:
     def  get_bar( self ):
         return  'wxy'
 
     # *必須兩個參數
     def  set_bar( self , value):
         return  return  'set value'  +  value
 
     def  del_bar( self ):
         return  'wxy'
 
     BAR =  property (get_bar, set_bar, del_bar,  'description...' )
 
obj  =  Foo()
 
obj.BAR               # 自動調用第一個參數中定義的方法:get_bar
obj.BAR  =  "korala"      # 自動調用第二個參數中定義的方法:set_bar方法,並將「korala」看成參數傳入
del  Foo.BAR           # 自動調用第三個參數中定義的方法:del_bar方法
obj.BAE.__doc__       # 自動獲取第四個參數中設置的值:description...
因爲靜態字段方式建立屬性具備三種訪問方式,咱們能夠根據他們幾個屬性的訪問特色,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class  Goods( object ):
     def  __init__( self ):
         # 原價
         self .original_price  =  100
         # 折扣
         self .discount  =  0.8
 
     def  get_price( self ):
         # 實際價格 = 原價 * 折扣
         new_price  =  self .original_price  *  self .discount
         return  new_price
 
     def  set_price( self , value):
         self .original_price  =  value
 
     def  del_price( self , value):
         del  self .original_price
 
     PRICE  =  property (get_price, set_price, del_price,  '價格屬性描述...' )
 
obj  =  Goods()
obj.PRICE          # 獲取商品價格
obj.PRICE  =  200    # 修改商品原價
del  obj.PRICE      # 刪除商品原價
注意:Python WEB框架 Django 的視圖中 request.POST 就是使用的靜態字段的方式建立的屬性。
# Django源碼
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class  WSGIRequest(http.HttpRequest):
     def  __init__( self , environ):
         script_name  =  get_script_name(environ)
         path_info  =  get_path_info(environ)
         if  not  path_info:
             # Sometimes PATH_INFO exists, but is empty (e.g. accessing
             # the SCRIPT_NAME URL without a trailing slash). We really need to
             # operate as if they'd requested '/'. Not amazingly nice to force
             # the path like this, but should be harmless.
             path_info  =  '/'
         self .environ  =  environ
         self .path_info  =  path_info
         self .path  =  '%s/%s'  %  (script_name.rstrip( '/' ), path_info.lstrip( '/' ))
         self .META  =  environ
         self .META[ 'PATH_INFO' =  path_info
         self .META[ 'SCRIPT_NAME' =  script_name
         self .method  =  environ[ 'REQUEST_METHOD' ].upper()
         _, content_params  =  cgi.parse_header(environ.get( 'CONTENT_TYPE' , ''))
         if  'charset'  in  content_params:
             try :
                 codecs.lookup(content_params[ 'charset' ])
             except  LookupError:
                 pass
             else :
                 self .encoding  =  content_params[ 'charset' ]
         self ._post_parse_error  =  False
         try :
             content_length  =  int (environ.get( 'CONTENT_LENGTH' ))
         except  (ValueError, TypeError):
             content_length  =  0
         self ._stream  =  LimitedStream( self .environ[ 'wsgi.input' ], content_length)
         self ._read_started  =  False
         self .resolver_match  =  None
 
     def  _get_scheme( self ):
         return  self .environ.get( 'wsgi.url_scheme' )
 
     def  _get_request( self ):
         warnings.warn( '`request.REQUEST` is deprecated, use `request.GET` or '
                       '`request.POST` instead.' , RemovedInDjango19Warning,  2 )
         if  not  hasattr ( self '_request' ):
             self ._request  =  datastructures.MergeDict( self .POST,  self .GET)
         return  self ._request
 
     @cached_property
     def  GET( self ):
         # The WSGI spec says 'QUERY_STRING' may be absent.
         raw_query_string  =  get_bytes_from_wsgi( self .environ,  'QUERY_STRING' , '')
         return  http.QueryDict(raw_query_string, encoding = self ._encoding)
     
     # ############### 看這裏看這裏  ###############
     def  _get_post( self ):
         if  not  hasattr ( self '_post' ):
             self ._load_post_and_files()
         return  self ._post
 
     # ############### 看這裏看這裏  ###############
     def  _set_post( self , post):
         self ._post  =  post
 
     @cached_property
     def  COOKIES( self ):
         raw_cookie  =  get_str_from_wsgi( self .environ,  'HTTP_COOKIE' , '')
         return  http.parse_cookie(raw_cookie)
 
     def  _get_files( self ):
         if  not  hasattr ( self '_files' ):
             self ._load_post_and_files()
         return  self ._files
 
     # ############### 看這裏看這裏  ###############
     POST  =  property (_get_post, _set_post)
     
     FILES  =  property (_get_files)
     REQUEST  =  property (_get_request)
因此,定義屬性共有兩種方式,分別是【裝飾器】和【靜態字段】,而【裝飾器】方式針對經典類和新式類又有所不一樣。 
小結:
屬性:將方法僞形成字段
定義方式:
裝飾器 即:在方法上應用裝飾器 @property
靜態字段 即:在類中定義值爲property對象的靜態字段
新式類屬性能夠設置讀寫刪;


類成員的修飾符

類的全部成員在上一步驟中已經作了詳細的介紹,對於每個類的成員而言都有兩種形式:

  • 公有成員,在任何地方都能訪問
  • 私有成員,只有在類的內部才能方法

私有成員和公有成員的定義不一樣:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)

1
2
3
4
class  C:
     def  __init__( self ):
         self .name  =  '公有字段'
         self .__foo  =  "私有字段"

靜態字段

  • 公有靜態字段:類能夠訪問;類內部能夠訪問;派生類中能夠訪問
  • 私有靜態字段:僅類內部能夠訪問;
# 公有靜態字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  C:
name  =  "公有靜態字段"
     def  func( self ):
         print  C.name
 
class  D(C):
     def  show( self ):
         print  C.name
 
C.name          # 類訪問
 
obj  =  C()
obj.func()      # 類內部能夠訪問
 
obj_son  =  D()
obj_son.show()  # 派生類中能夠訪問
# 私有靜態字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  C:
     __name  =  "私有靜態字段"
     def  func( self ):
         print  C.__name
 
class  D(C):
     def  show( self ):
         print  C.__name
 
C.__name        # 類訪問            ==> 錯誤
 
obj  =  C()
obj.func()      # 類內部能夠訪問     ==> 正確
 
obj_son  =  D()
obj_son.show()  # 派生類中能夠訪問   ==> 錯誤

普通字段

  • 公有普通字段:對象能夠訪問;類內部能夠訪問;派生類中能夠訪問
  • 私有普通字段:僅類內部能夠訪問;

ps若是想要強制訪問私有字段,能夠經過 【對象._類名__私有字段明 】訪問(如:obj._C__foo),不建議強制訪問私有成員。

# 公有字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  C:
     def  __init__( self ):
         self .foo  =  "公有字段"
 
     def  func( self ):
         print  self .foo   # 類內部訪問
 
class  D(C):
     def  show( self ):
         print  self .foo # 派生類中訪問
 
obj  =  C()
 
obj.foo      # 經過對象訪問
obj.func()   # 類內部訪問
 
obj_son  =  D();
obj_son.show()   # 派生類中訪問
# 私有字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  C:
     def  __init__( self ):
         self .__foo  =  "私有字段"
 
     def  func( self ):
         print  self .foo   # 類內部訪問
 
class  D(C):
     def  show( self ):
         print  self .foo # 派生類中訪問
 
obj  =  C()
 
obj.__foo      # 經過對象訪問    ==> 錯誤
obj.func()   # 類內部訪問        ==> 正確
 
obj_son  =  D();
obj_son.show()   # 派生類中訪問  ==> 錯誤

方法、屬性的訪問於上述方式類似,即:私有成員只能在類內部使用

ps:非要訪問私有屬性的話,能夠經過 對象._類__屬性名

 

類的特殊成員

上文介紹了Python的類成員以及成員修飾符,從而瞭解到類中有字段、方法和屬性三大類成員,而且成員名前若是有兩個下劃線,則表示該成員是私有成員,私有成員只能由類內部調用。不管人或事物每每都有不按套路出牌的狀況,Python的類成員也是如此,存在着一些具備特殊含義的成員,詳情以下:

1. __doc__

  表示類的描述信息。

1
2
3
4
5
6
7
class  Foo:
     """ 描述類信息,這是用於看片的神奇 """
     def  func( self ):
         pass
 
print  Foo.__doc__
#輸出:類的描述信息

2. __module__  和 __class__ 

  __module__ 表示當前操做的對象在那個模塊

  __class__ 表示當前操做的對象的類是什麼

# lib/aa.py
# index.py
1
2
3
4
5
from  lib.aa  import  C
 
obj  =  C()
print  obj.__module__   # 輸出 lib.aa,即:輸出模塊
print  obj.__class__       # 輸出 lib.aa.C,即:輸出類

3. __init__

  構造方法,經過類建立對象時,自動觸發執行。

1
2
3
4
5
6
class  Foo:
     def  __init__( self , name):
         self .name  =  name
         self .age  =  18
 
obj  =  Foo( 'wupeiqi' # 自動執行類中的 __init__ 方法

4. __del__

  析構方法,當對象在內存中被釋放時,自動觸發執行。

注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

1
2
3
class  Foo:
     def  __del__( self ):
         pass

5. __call__

  對象後面加括號,觸發執行。

注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

1
2
3
4
5
6
7
8
9
class  Foo:
     def  __init__( self ):
         pass
     
     def  __call__( self * args,  * * kwargs):
         print  '__call__'
 
obj  =  Foo()  # 執行 __init__
obj()        # 執行 __call__

6. __dict__

  類或對象中的全部成員

上文中咱們知道:類的普通字段屬於對象;類中的靜態字段和方法等屬於類,即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  Province:
     country  =  'China'
     def  __init__( self , name, count):
         self .name  =  name
         self .count  =  count
 
     def  func( self * args,  * * kwargs):
         print  'func'
 
# 獲取類的成員,即:靜態字段、方法、
print  Province.__dict__
# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
 
obj1  =  Province( 'HeBei' , 10000 )
print  obj1.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 10000, 'name': 'HeBei'}
 
obj2  =  Province( 'HeNan' 3888 )
print  obj2.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 3888, 'name': 'HeNan'}

7. __str__

  若是一個類中定義了__str__方法,那麼在打印 對象 時,默認輸出該方法的返回值。

1
2
3
4
5
6
7
class  Foo:
     def  __str__( self ):
         return  'korala'
 
obj  =  Foo()
print  obj
# 輸出korala

8、__getitem__、__setitem__、__delitem__

用於索引操做,如字典。以上分別表示獲取、設置、刪除數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
class  Foo( object ):
     def  __getitem__( self , key):
         print  '__getitem__' ,key
  
     def  __setitem__( self , key, value):
         print  '__setitem__' ,key,value
  
     def  __delitem__( self , key):
         print  '__delitem__' ,key
   
obj  =  Foo()
  
result  =  obj[ 'k1' ]       # 自動觸發執行 __getitem__
obj[ 'k2' =  'korala'    # 自動觸發執行 __setitem__
del  obj[ 'k1' ]            # 自動觸發執行 __delitem__

9、__getslice__、__setslice__、__delslice__

 該三個方法用於分片操做,如:列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
class  Foo( object ):
     def  __getslice__( self , i, j):
         print  '__getslice__' ,i,j
  
     def  __setslice__( self , i, j, sequence):
         print  '__setslice__' ,i,j
  
     def  __delslice__( self , i, j):
         print  '__delslice__' ,i,j
  
obj  =  Foo()
  
obj[ - 1 : 1 ]                    # 自動觸發執行 __getslice__
obj[ 0 : 1 =  [ 11 , 22 , 33 , 44 ]     # 自動觸發執行 __setslice__
del  obj[ 0 : 2 ]                 # 自動觸發執行 __delslice__

10. __iter__ 

用於迭代器,之因此列表、字典、元組能夠進行for循環,是由於類型內部定義了 __iter__。

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
30
31
32
33
34
35
36
37
38
39
40
41
# 第一步
class  Foo( object ):
     pass
         
obj  =  Foo()
 
for  in  obj:
     print  i
     
# 報錯:TypeError: 'Foo' object is not iterable
 
# 第二步
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
class  Foo( object ):
     def  __iter__( self ):
         pass
 
obj  =  Foo()
 
for  in  obj:
     print  i
 
# 報錯:TypeError: iter() returned non-iterator of type 'NoneType'
 
# 第三步
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
class  Foo( object ):
     def  __init__( self , sq):
         self .sq  =  sq
 
     def  __iter__( self ):
         return  iter ( self .sq)
 
obj  =  Foo([ 11 , 22 , 33 , 44 ])
 
for  in  obj:
     print  i

以上步驟能夠看出,for循環迭代的實際上是  iter([11,22,33,44]) ,因此執行流程能夠變動爲:

1
2
3
4
5
6
#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
obj  =  iter ([ 11 , 22 , 33 , 44 ])
for  in  obj:
     print  i

11. __new__ 和 __metaclass__

閱讀如下代碼:

1
2
3
4
class  Foo( object ):
     def  __init__( self ):
         pass
obj  =  Foo()    # obj是經過Foo類實例化的對象

上述代碼中,obj 是經過 Foo 類實例化的對象,其實,不只 obj 是一個對象,Foo類自己也是一個對象,由於在Python中一切事物都是對象

若是按照一切事物都是對象的理論:obj對象是經過執行Foo類的構造方法建立,那麼Foo類對象應該也是經過執行某個類的 構造方法 建立。

1
2
print  type (obj)  # 輸出:<class '__main__.Foo'>     表示,obj 對象由Foo類建立
print  type (Foo)  # 輸出:<type 'type'>              表示,Foo類對象由 type 類建立

因此,obj對象是Foo類的一個實例Foo類對象是 type 類的一個實例,即:Foo類對象 是經過type類的構造方法建立。

那麼,建立類就能夠有兩種方式:

a). 普通方式

1
2
3
class  Foo( object ):
     def  func( self ):
         print  'hello korala'

b).特殊方式(type類的構造函數)

1
2
3
4
5
6
7
ef func( self ):
     print  'hello korala'
  
Foo  =  type ( 'Foo' ,( object ,), { 'func' : func})
#type第一個參數:類名
#type第二個參數:當前類的基類
#type第三個參數:類的成員<span style="font-family: 宋體; font-size: 16px; color: #ff0000;"><strong> </strong></span>
 

==》 類 是由 type 類實例化產生

 

那麼問題來了,類默認是由 type 類實例化產生,type類中如何實現的建立類?類又是如何建立對象?

 

答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化建立,因此,咱們能夠爲 __metaclass__ 設置一個type類的派生類,從而查看 類 建立的過程。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class  MyType( type ):
     def  __init__( self , what, bases = None dict = None ):
         super (MyType,  self ).__init__(what, bases,  dict )
 
     def  __call__( self * args,  * * kwargs):
         obj  =  self .__new__( self * args,  * * kwargs)
 
         self .__init__(obj)
 
class  Foo( object ):
     __metaclass__  =  MyType
 
     def  __init__( self , name):
         self .name  =  name
 
     def  __new__( cls * args,  * * kwargs):
         return  object .__new__( cls * args,  * * kwargs)
 
# 第一階段:解釋器從上到下執行代碼建立Foo類
# 第二階段:經過Foo類建立obj對象
obj  =  Foo()

 

總結:一、面向對象的三大特性     封裝、繼承、多態(用不到)二、python ——> 封裝    兩種狀況:(1)多個方法共用一組變量,變量封裝到對象中;             (2)遊戲 模板三、繼承    (1)基類、派生類    (2)多繼承            新式類、經典類            廣度優先(規則)、深度優先 ==> 面試四、類,對象,內存 ==> 關係圖五、類成員     字段:普通(對象)、靜態(類)     方法:普通(對象觸發)至少一個self,self=當前對象           類 ==> 類觸發 只有一個cls,cls=當前類           靜態 ==> 類觸發  任意參數     屬性:方法的變種,變成訪問時跟字段類似         定義方式:@property                  data = property(方法名)                      # obj.data = 執行方法,並獲取方法的返回值         新式類中的屬性:            @property 修改            @方法名.setter 設置            @方法名.deleter 刪除六、類成員修飾符    公有    私有 以"__"開頭,只能內部訪問         非要訪問,對象._類名__成員七、對象後面加括號執行__call__方法,類後面加括號執行__init__方法八、__dict__ 類或對象中的全部成員對象由類建立,一切事物都是對象 ==> 類也是對象對象.__dict__ 查看對象裏有什麼東西類.__dict__ 查看類裏有什麼東西九、__str__輸出時會輸出返回值對象轉成字符串,類裏找方法,並獲取返回值

相關文章
相關標籤/搜索