python009 -- 類

什麼是面向對象編程?

面向對象編程(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():
    = 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 12in <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 17in <module>
 print mc.funcA()
 File "test2.py", line 7in 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.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.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 15in <module>
    mc.func('22')
  File "test2.py", line 11in func
    print super(Child, self).func(age)
TypeError: must be typenot 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 1in <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)
(123)
{'a'1'c'3'b'2}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章