什麼是面向對象編程?
面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)是一種計算機編程架構。Python就是這種編程語言。
面向對象程序設計中的概念主要包括:對象、類、繼承、動態綁定、封裝、多態性、消息傳遞、方法。
1)對象:類的實體,比如一個人。
2)類:一個共享相同結構和行爲的對象的集合。通俗的講就是分類,比如人是一類,動物是一類。
3)繼承:類之間的關係,比如貓狗是一類,他們都有四條腿,狗繼承了這個四條腿,擁有了這個屬性。
4)動態綁定:在不修改源碼情況下,動態綁定方法來給實例增加功能。
5)封裝:把相同功能的類方法、屬性封裝到類中,比如人兩條腿走路,狗有四條腿走路,兩個不能封裝到一個類中。
6)多態性:一個功能可以表示不同類的對象,任何對象可以有不同的方式操作。比如一個狗會走路、會跑。
7)消息傳遞:一個對象調用了另一個對象的方法。
8)方法:類裏面的函數,也稱爲成員函數。
對象=屬性+方法。
屬性:變量。
方法:函數。
實例化:創建一個類的具體實例對象。比如一條泰迪。
什麼是類?
類是對對象的抽象,對象是類的實體,是一種數據類型。它不存在內存中,不能被直接操作,只有被實例化對象時,纔會變的可操作。
類是對現實生活中一類具有共同特徵的事物的抽象描述。
6.1 類和類方法語法
1
2
3
4
5
6
|
# 類 class ClassName(): pass # 類中的方法 def funcName( self ): pass |
self代表類本身。類中的所有的函數的第一個參數必須是self。
6.2 類定義與調用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): x = 100 def func( self , name): return "Hello %s!" % name def func2( self ): return self .x mc = MyClass() # 類實例化,綁定到變量mc print mc.x # 類屬性引用 print mc.func( "xiaoming" ) # 調用類方法 print mc.func2() # python test.py 100 Hello xiaoming! 100 |
上面示例中,x變量稱爲類屬性,類屬性又分爲類屬性和實例屬性:
1)類屬性屬於類本身,通過類名訪問,一般作爲全局變量。比如mc.x
2)如果類方法想調用類屬性,需要使用self關鍵字調用。比如self.x
3)實例屬性是實例化後對象的方法和屬性,通過實例訪問,一般作爲局部變量。下面會講到。
4)當實例化後可以動態類屬性,下面會講到。
類方法調用:
1)類方法之間調用:self.<方法名>(參數),參數不需要加self
2)外部調用:<實例名>.<方法名>
6.3 類的說明
給類添加註釋,提高可閱讀性,可以通過下面方式查看。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
方法 1 : >>> class MyClass: ... """ ... 這是一個測試類. ... """ ... pass ... >>> print MyClass.__doc__ 這是一個測試類. >>> 方法 2 : >>> help (MyClass) Help on class MyClass in module __main__: class MyClass | 這是一個測試類. |
6.4 類內置方法
__init__(self, ...) | 初始化對象,在創建新對象時調用 |
__del__(self) | 釋放對象,在對象被刪除之前調用 |
__new__(cls, *args, **kwd) | 實例的生成操作,在__init__(self)之前調用 |
__str__(self) | 在使用print語句時被調用,返回一個字符串 |
__getitem__(self, key) | 獲取序列的索引key對應的值,等價於seq[key] |
__len__(self) | 在調用內建函數len()時被調用 |
__cmp__(str, dst) | 比較兩個對象src和dst |
__getattr__(s, name) | 獲取屬性的值 |
__setattr__(s, name, value) | 設置屬性的值 |
__delattr__(s, name) | 刪除屬性 |
__gt__(self, other) | 判斷self對象是否大於other對象 |
__lt__(self, other) | 判斷self對象是否小於other對象 |
__ge__(self, other) | 判斷self對象是否大於或等於other對象 |
__le__(self, other) | 判斷self對象是否小於或等於other對象 |
__eq__(self, other) | 判斷self對象是否等於other對象 |
__call__(self, *args) | 把實例對象作爲函數調用 |
6.5 初始化實例屬性
很多類一般都有初始狀態的,常常定義對象的共同特性,也可以用來定義一些你希望的初始值。
Python類中定義了一個構造函數__init__,對類中的實例定義一個初始化對象,常用於初始化類變量。當類被實例化,第二步自動調用的函數,第一步是__new__函數。
__init__構造函數也可以讓類傳參,類似於函數的參數。
__init__構造函數使用:
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): def __init__( self ): self .name = "xiaoming" def func( self ): return self .name mc = MyClass() print mc.func() # python test.py xiaoming |
__init__函數定義到類的開頭.self.name變量是一個實例屬性,只能在類方法中使用,引用時也要這樣self.name。
類傳參:
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): def __init__( self , name): self .name = name def func( self , age): return "name: %s,age: %s" % ( self .name, age) mc = MyClass( 'xiaoming' ) # 第一個參數是默認定義好的傳入到了__init__函數 print mc.func( '22' ) # python test.py Name: xiaoming, Age: 22 |
博客地址:http://lizhenliang.blog.51cto.com
QQ羣:Shell/Python運維開發羣 323779636
6.6 類私有化(私有屬性)
6.6.1 單下劃線
實現模塊級別的私有化,以單下劃線開頭的變量和函數只能類或子類才能訪問。當from modulename import * 時將不會引入以單下劃線卡頭的變量和函數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): _age = 21 def __init__( self , name = None ): self ._name = name def func( self , age): return "Name: %s, Age: %s" % ( self ._name, age) mc = MyClass( 'xiaoming' ) print mc.func( '22' ) print mc._name print mc._age # python test.py Name: xiaoming, Age: 22 xiaoming 21 |
_age和self._name變量其實就是做了個聲明,說明這是個內部變量,外部不要去引用它。
6.6.2 雙下劃線
以雙下劃線開頭的變量,表示私有變量,受保護的,只能類本身能訪問,連子類也不能訪問。避免子類與父類同名屬性衝突。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): __age = 21 def __init__( self , name = None ): self .__name = name def func( self , age): return "Name: %s, Age: %s" % ( self .__name, age) mc = MyClass( 'xiaoming' ) print mc.func( '22' ) print mc.__name print mc.__age # python test.py Name: xiaoming, Age: 22 Traceback (most recent call last): File "test.py" , line 12 , in <module> print mc.__name AttributeError: MyClass instance has no attribute '__name' |
可見,在單下劃線基礎上又加了一個下劃線,同樣方式類屬性引用,出現報錯。說明雙下劃線變量只能本身能用。
如果想訪問私有變量,可以這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#!/usr/bin/python # -*- coding: utf-8 -*- class MyClass(): __age = 21 def __init__( self , name = None ): self .__name = name def func( self , age): return "Name: %s, Age: %s" % ( self .__name, age) mc = MyClass( 'xiaoming' ) print mc.func( '22' ) print mc._MyClass__name print mc._MyClass__age # python test.py Name: xiaoming, Age: 22 xiaoming 21 |
self.__name變量編譯成了self._MyClass__name,以達到不能被外部訪問的目的,並沒有真正意義上的私有。
6.6.3 特殊屬性(首尾雙下劃線)
一般保存對象的元數據,比如__doc__、__module__、__name__:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> class MyClass: """ 這是一個測試類說明的類。 """ pass # dic()返回對象內變量、方法 >>> dir (MyClass) [ '__doc__' , '__module__' ] >>> MyClass.__doc__ '\n\t\xd5\xe2\xca\xc7\xd2\xbb\xb8\xf6\xb2\xe2\xca\xd4\xc0\xe0\xcb\xb5\xc3\xf7\xb5\xc4\xc0\xe0\xa1\xa3\n\t' >>> MyClass.__module__ '__main__' >>> MyClass.__name__ 'MyClass' |
這裏用到了一個新內置函數dir(),不帶參數時,返回當前範圍內的變量、方法的列表。帶參數時,返回參數的屬性、方法的列表。
Python自己調用的,而不是用戶來調用。像__init__ ,你可以重寫。
6.7 類的繼承
子類繼承父類,子類將繼承父類的所有方法和屬性,提高代碼重用。
1)簡單繼承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self , name = None ): self .name = name def func( self , age): return "Name: %s, Age: %s" % ( self .name, age) class Child(Parent): pass mc = Child( 'xiaoming' ) print mc.func( '22' ) print mc.name # python test.py Name: xiaoming, Age: 22 xiaoming |
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
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self ): self .name_a = "xiaoming" def funcA( self ): return "function A: %s" % self .name_a class Child(Parent): def __init__( self ): self .name_b = "zhangsan" def funcB( self ): return "function B: %s" % self .name_b mc = Child() print mc.name_b print mc.funcB() print mc.funcA() # python test.py zhangsan function B: zhangsan Traceback (most recent call last): File "test2.py" , line 17 , in <module> print mc.funcA() File "test2.py" , line 7 , in funcA return "function A: %s" % self .name_a AttributeError: Child instance has no attribute 'name_a' |
拋出錯誤,提示調用funcA()函數時,沒有找到name_a屬性,也就說明了父類的構造函數並沒有執行。
如果想解決這個問題,可通過下面兩種方法:
方法1:調用父類構造函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self ): self .name_a = "xiaoming" def funcA( self ): return "function A: %s" % self .name_a class Child(Parent): def __init__( self ): Parent.__init__( self ) self .name_b = "zhangsan" def funcB( self ): return "function B: %s" % self .name_b mc = Child() print mc.name_b print mc.funcB() print mc.funcA() # python test.py zhangsan function B: zhangsan function A: xiaoming |
方法2:使用supper()函數繼承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent( object ): def __init__( self ): self .name_a = "xiaoming" def funcA( self ): return "function A: %s" % self .name_a class Child(Parent): def __init__( self ): super (Child, self ).__init__() self .name_b = "zhangsan" def funcB( self ): return "function B: %s" % self .name_b mc = Child() print mc.name_b print mc.funcB() print mc.funcA() # python test.py zhangsan function B: zhangsan function A: xiaoming |
6.8 多重繼承
每個類可以擁有多個父類,如果調用的屬性或方法在子類中沒有,就會從父類中查找。多重繼承中,是依次按順序執行。
類簡單的繼承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#!/usr/bin/python # -*- coding: utf-8 -*- class A: def __init__( self ): self .var1 = "var1" self .var2 = "var2" def a( self ): print "a..." class B: def b( self ): print "b..." class C(A,B): pass c = C() c.a() c.b() print c.var1 print c.var2 # python test.py a... b... var1 var2 |
類C繼承了A和B的屬性和方法,就可以像使用父類一樣使用它。
子類擴展方法,直接在子類中定義即可:
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
|
#!/usr/bin/python # -*- coding: utf-8 -*- class A: def __init__( self ): self .var1 = "var1" self .var2 = "var2" def a( self ): print "a..." class B: def b( self ): print "b..." class C(A,B): def test( self ): print "test..." c = C() c.a() c.b() c.test() print c.var1 print c.var2 # python test.py a... b... test... var1 var2 |
在這說明下經典類和新式類。
經典類:默認沒有父類,也就是沒繼承類。
新式類:有繼承的類,如果沒有,可以繼承object。在Python3中已經默認繼承object類。
經典類在多重繼承時,採用從左到右深度優先原則匹配,而新式類是採用C3算法(不同於廣度優先)進行匹配。兩者主要區別在於遍歷父類算法不同,具體些請在網上查資料。
6.9 方法重載
直接定義和父類同名的方法,子類就修改了父類的動作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self , name = 'xiaoming' ): self .name = name def func( self , age): return "Name: %s, Age: %s" % ( self .name, age) class Child(Parent): def func( self , age = 22 ): return "Name: %s, Age: %s" % ( self .name, age) mc = Child() print mc.func() # python test.py Name: xiaoming, Age: 22 |
6.10 修改父類方法
在方法重載中調用父類的方法,實現添加功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self , name = 'xiaoming' ): self .name = name def func( self , age): return "Name: %s, Age: %s" % ( self .name, age) class Child(Parent): def func( self , age): print "------" print Parent.func( self , age) # 調用父類方法 print "------" mc = Child() mc.func( '22' ) # python test.py - - - - - - Name: xiaoming, Age: 22 - - - - - - |
還有一種方式通過super函數調用父類方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent(): def __init__( self , name = 'xiaoming' ): self .name = name def func( self , age): return "Name: %s, Age: %s" % ( self .name, age) class Child(Parent): def func( self , age): print "------" print super (Child, self ).func(age) print "------" mc = Child() mc.func( '22' ) # python test.py - - - - - - Traceback (most recent call last): File "test2.py" , line 15 , in <module> mc.func( '22' ) File "test2.py" , line 11 , in func print super (Child, self ).func(age) TypeError: must be type , not classobj |
拋出錯誤,因爲super繼承只能用於新式類,用於經典類就會報錯。
那我們就讓父類繼承object就可以使用super函數了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Parent( object ): def __init__( self , name = 'xiaoming' ): self .name = name def func( self , age): return "Name: %s, Age: %s" % ( self .name, age) class Child(Parent): def func( self , age): print "------" print super (Child, self ).func(age) # 調用父類方法。在Python3中super參數可不用寫。 print "------" mc = Child() mc.func( '22' ) # python test.py - - - - - - Name: xiaoming, Age: 22 - - - - - - |
6.11 屬性訪問的特殊方法
有四個可對類對象增刪改查的內建函數,分別是getattr()、hasattr()、setattr()、delattr()。
6.11.1 getattr()
返回一個對象屬性或方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> class A: ... def __init__( self ): ... self .name = 'xiaoming' ... def method( self ): ... print "method..." ... >>> c = A() >>> getattr (c, 'name' , 'Not find name!' ) 'xiaoming' >>> getattr (c, 'namea' , 'Not find name!' ) >>> getattr (c, 'method' , 'Not find method!' ) <bound method A.method of <__main__.A instance at 0x93fa70 >> >>> getattr (c, 'methoda' , 'Not find method!' ) 'Not find method!' |
6.11.2 hasattr()
判斷一個對象是否具有屬性或方法。返回一個布爾值。
1
2
3
4
5
6
7
8
|
>>> hasattr (c, 'name' ) True >>> hasattr (c, 'namea' ) False >>> hasattr (c, 'method' ) True >>> hasattr (c, 'methoda' ) False |
6.11.3 setattr()
給對象屬性重新賦值或添加。如果屬性不存在則添加,否則重新賦值。
1
2
3
4
5
6
7
|
>>> hasattr (c, 'age' ) False >>> setattr (c, 'age' , 22 ) >>> c.age 22 >>> hasattr (c, 'age' ) True |
6.11.4 delattr()
刪除對象屬性。
1
2
3
|
>>> delattr (c, 'age' ) >>> hasattr (c, 'age' ) False |
6.12 類裝飾器
與函數裝飾器類似,不同的是類要當做函數一樣調用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/usr/bin/python # -*- coding: utf-8 -*- class Deco: def __init__( self , func): self ._func = func self ._func_name = func.__name__ def __call__( self ): return self ._func(), self ._func_name @Deco def f1(): return "Hello world!" print f1() # python test.py ( 'Hello world!' , 'f1' ) |
6.13 類內置裝飾器
下面介紹類函數裝飾器,在實際開發中,感覺不是很常用。
6.10.1 @property
@property屬性裝飾器的基本功能是把類中的方法當做屬性來訪問。
在沒使用屬性裝飾器時,類方法是這樣被調用的:
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> class A: ... def __init__( self , a, b): ... self .a = a ... self .b = b ... def func( self ): ... print self .a + self .b ... >>> c = A( 2 , 2 ) >>> c.func() 4 >>> c.func <bound method A.func of <__main__.A instance at 0x7f6d962b1878 >> |
使用屬性裝飾器就可以像屬性那樣訪問了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> class A: ... def __init__( self , a, b): ... self .a = a ... self .b = b ... @ property ... def func( self ): ... print self .a + self .b ... >>> c = A( 2 , 2 ) >>> c.func 4 >>> c.func() 4 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> TypeError: 'NoneType' object is not callable |
6.10.2 @staticmethod
@staticmethod是靜態方法裝飾器,可以通過類對象訪問,也可以通過實例化後類對象實例訪問。
實例方法的第一個參數是self,表示是該類的一個實例,稱爲類對象實例。
而使用靜態方法裝飾器,第一個參數就不用傳入實例本身(self),那麼這個方法當做類對象,由Python自身處理。
看看普通方法的用法:
1
2
3
4
5
6
7
|
>>> class A: ... def staticMethod ( self ): ... print "not static method..." ... >>> c = A() >>> c. staticMethod () not static method... |
使用靜態方法則是這麼用:
1
2
3
4
5
6
7
8
9
10
|
>>> class A: ... @ staticmethod ... def staticMethod (): ... print "static method..." ... >>> A. staticMethod () # 可以通過類調用靜態方法 static method... >>> c = A() >>> c. staticMethod () # 還可以使用普通方法調用 static method... |
靜態方法和普通的非類方法作用一樣,只不過命名空間是在類裏面,必須通過類來調用。一般與類相關的操作使用靜態方法。
6.10.3 @classmethod
@classmethod是類方法裝飾器,與靜態方法裝飾器類似,也可以通過類對象訪問。主要區別在於類方法的第一個參數要傳入類對象(cls)。
1
2
3
4
5
6
7
8
9
|
>>> class A: ... @ classmethod ... def classMethod ( cls ): ... print "class method..." ... print cls .__name__ ... >>> A. classMethod () class method... A |
6.14 __call__方法
可以讓類中的方法像函數一樣調用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
>>> class A: ... def __call__( self , x): ... print "call..." ... print x ... >>> c = A() >>> c( 123 ) call... 123 >>> class A: ... def __call__( self , * args, * * kwargs): ... print args ... print kwargs ... >>> c = A() >>> c( 1 , 2 , 3 ,a = 1 ,b = 2 ,c = 3 ) ( 1 , 2 , 3 ) { 'a' : 1 , 'c' : 3 , 'b' : 2 } |