天天看點

第六章 Python類(面向對象程式設計)

<b>什麼是面向對象程式設計?</b>

   面向對象程式設計(object oriented programming,oop,面向對象程式設計)是一種計算機程式設計架構。python就是這種程式設計語言。

   面向對象程式設計中的概念主要包括:對象、類、繼承、動态綁定、封裝、多态性、消息傳遞、方法。

   1)對象:類的實體,比如一個人。

   2)類:一個共享相同結構和行為的對象的集合。通俗的講就是分類,比如人是一類,動物是一類。

   3)繼承:類之間的關系,比如貓狗是一類,他們都有四條腿,狗繼承了這個四條腿,擁有了這個屬性。

   4)動态綁定:在不修改源碼情況下,動态綁定方法來給執行個體增加功能。

   5)封裝:把相同功能的類方法、屬性封裝到類中,比如人兩條腿走路,狗有四條腿走路,兩個不能封裝到一個類中。

   6)多态性:一個功能可以表示不同類的對象,任何對象可以有不同的方式操作。比如一個狗會走路、會跑。

   7)消息傳遞:一個對象調用了另一個對象的方法。

   8)方法:類裡面的函數,也稱為成員函數。

   對象=屬性+方法。

   屬性:變量。

   方法:函數。

   執行個體化:建立一個類的具體執行個體對象。比如一條泰迪。

<b>什麼是類?</b>

   類是對對象的抽象,對象是類的實體,是一種資料類型。它不存在記憶體中,不能被直接操作,隻有被執行個體化對象時,才會變的可操作。

   類是對現實生活中一類具有共同特征的事物的抽象描述。

<b>6.1 類和類方法文法</b>

# 類

class classname():

    pass

# 類中的方法

def funcname(self):

       pass

self代表類本身。類中的所有的函數的第一個參數必須是self。

<b>6.2 類定義與調用</b>

#!/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!

上面示例中,x變量稱為類屬性,類屬性又分為類屬性和執行個體屬性:

   1)類屬性屬于類本身,通過類名通路,一般作為全局變量。比如mc.x

   2)如果類方法想調用類屬性,需要使用self關鍵字調用。比如self.x

   3)執行個體屬性是執行個體化後對象的方法和屬性,通過執行個體通路,一般作為局部變量。下面會講到。

   4)當執行個體化後可以動态類屬性,下面會講到。

類方法調用:

1)類方法之間調用:self.&lt;方法名&gt;(參數),參數不需要加self

2)外部調用:&lt;執行個體名&gt;.&lt;方法名&gt;

<b>6.3 類的說明</b>

給類添加注釋,提高可閱讀性,可通過下面方式檢視。

方法1:

&gt;&gt;&gt; class myclass:

...   """

...   這是一個測試類.

...   pass

...

&gt;&gt;&gt; print myclass.__doc__

  這是一個測試類.

&gt;&gt;&gt;

方法2:

&gt;&gt;&gt; help(myclass)

help on class myclass in module __main__:

class myclass

 |  這是一個測試類.

<b>6.4 類内置方法</b>

<b>内置方法</b>

<b>描述</b>

__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)

把執行個體對象作為函數調用

<b>6.5 初始化執行個體屬性</b>

   很多類一般都有初始狀态的,常常定義對象的共同特性,也可以用來定義一些你希望的初始值。

   python類中定義了一個構造函數__init__,對類中的執行個體定義一個初始化對象,常用于初始化類變量。當類被執行個體化,第二步自動調用的函數,第一步是__new__函數。

   __init__構造函數也可以讓類傳參,類似于函數的參數。

   __init__構造函數使用:

    def __init__(self):

        self.name = "xiaoming"

    def func(self):

        return self.name

mc = myclass()

print mc.func()

xiaoming

   __init__函數定義到類的開頭.self.name變量是一個執行個體屬性,隻能在類方法中使用,引用時也要這樣self.name。

   類傳參:

    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') 

name: xiaoming, age: 22

<b>6.6 類私有化(私有屬性)</b>

<b>   6.6.1 單下劃線</b>

   實作子產品級别的私有化,以單下劃線開頭的變量和函數隻能類或子類才能通路。當from modulename import * 時将不會引入以單下劃線卡頭的變量和函數。

    _age = 21

    def __init__(self, name=none):

        self._name = name

        return "name: %s, age: %s" %(self._name, age)

mc = myclass('xiaoming')

print mc.func('22')

print mc._name

print mc._age

21

   _age和self._name變量其實就是做了個聲明,說明這是個内部變量,外部不要去引用它。

<b>   6.6.2 雙下劃線</b>

   以雙下劃線開頭的變量,表示私有變量,受保護的,隻能類本身能通路,連子類也不能通路。避免子類與父類同名屬性沖突。

      __age = 21

      def __init__(self, name=none):

          self.__name = name

      def func(self, age):

          return "name: %s, age: %s" %(self.__name, age)

print mc.__name

print mc.__age

# python test.py

traceback (most recent call last):

  file "test.py", line 12, in &lt;module&gt;

    print mc.__name

attributeerror: myclass instance has no attribute '__name'

   可見,在單下劃線基礎上又加了一個下劃線,同樣方式類屬性引用,出現報錯。說明雙下劃線變量隻能本身能用。

   如果想通路私有變量,可以這樣:

    __age = 21

        self.__name = name

        return "name: %s, age: %s" %(self.__name, age)

print mc._myclass__name

print mc._myclass__age

   self.__name變量編譯成了self._myclass__name,以達到不能被外部通路的目的,并沒有真正意義上的私有。

<b>   6.6.3 特殊屬性(首尾雙下劃線)</b>

   一般儲存對象的中繼資料,比如__doc__、__module__、__name__:

    """

    這是一個測試類說明的類。

# dic()傳回對象内變量、方法

&gt;&gt;&gt; dir(myclass)

['__doc__', '__module__']

&gt;&gt;&gt; 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'

&gt;&gt;&gt; myclass.__module__

'__main__'

&gt;&gt;&gt; myclass.__name__

'myclass'

   這裡用到了一個新内置函數dir(),不帶參數時,傳回目前範圍内的變量、方法的清單。帶參數時,傳回參數的屬性、方法的清單。

python自己調用的,而不是使用者來調用。像__init__ ,你可以重寫。

部落格位址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang

qq群:323779636(shell/python運維開發群)

<b>6.7 類的繼承</b>

子類繼承父類,子類将繼承父類的所有方法和屬性,提高代碼重用。

   <b>1)簡單繼承</b>

class parent():

        return "name: %s, age: %s" %(self.name, age)

class child(parent):

mc = child('xiaoming')

print mc.name

<b>   2)子類執行個體初始化</b>

   如果子類重寫了構造函數,那麼父類的構造函數将不會執行:

        self.name_a = "xiaoming"

    def funca(self):

        return "function a: %s" % self.name_a

        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()

zhangsan

function b: zhangsan

  file "test2.py", line 17, in &lt;module&gt;

    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:調用父類構造函數

        parent.__init__(self)

function a: xiaoming

   方法2:使用supper()函數繼承

class parent(object):

        super(child, self).__init__()

<b>6.8 多重繼承</b>

每個類可以擁有多個父類,如果調用的屬性或方法在子類中沒有,就會從父類中查找。多重繼承中,是依次按順序執行。

類簡單的繼承:

class a:

        self.var1 = "var1"

        self.var2 = "var2"

    def a(self):

        print "a..."

class b:

    def b(self):

        print "b..."

class c(a,b):

c = c()

c.a()

c.b()

print c.var1

print c.var2

a...

b...

var1

var2

類c繼承了a和b的屬性和方法,就可以像使用父類一樣使用它。

子類擴充方法,直接在子類中定義即可:

    def test(self):

        print "test..."

c.test()

test...

在這說明下經典類和新式類。

經典類:預設沒有父類,也就是沒繼承類。

新式類:有繼承的類,如果沒有,可以繼承object。在python3中已經預設繼承object類。

經典類在多重繼承時,采用從左到右深度優先原則比對,而新式類是采用c3算法(不同于廣度優先)進行比對。兩者主要差別在于周遊父類算法不同,具體些請在網上查資料。

<b>6.9 方法重載</b>

直接定義和父類同名的方法,子類就修改了父類的動作。

    def __init__(self, name='xiaoming'):

    def func(self, age=22):

<b>6.10 修改父類方法</b>

在方法重載中調用父類的方法,實作添加功能。

        print "------"

        print parent.func(self, age)   # 調用父類方法

mc.func('22')

------

還有一種方式通過super函數調用父類方法:

        print super(child, self).func(age)

  file "test2.py", line 15, in &lt;module&gt;

    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函數了:

        print super(child, self).func(age)   # 調用父類方法。在python3中super參數可不用寫。

<b>6.11 屬性通路的特殊方法</b>

有四個可對類對象增删改查的内建函數,分别是getattr()、hasattr()、setattr()、delattr()。

<b>   6.11.1 getattr()</b>

   傳回一個對象屬性或方法。

&gt;&gt;&gt; class a:

...   def __init__(self):

...     self.name = 'xiaoming'

...   def method(self):

...     print "method..."

&gt;&gt;&gt; c = a()

&gt;&gt;&gt; getattr(c, 'name', 'not find name!')   

'xiaoming'

&gt;&gt;&gt; getattr(c, 'namea', 'not find name!')

&gt;&gt;&gt; getattr(c, 'method', 'not find method!')

&lt;bound method a.method of &lt;__main__.a instance at 0x93fa70&gt;&gt;

&gt;&gt;&gt; getattr(c, 'methoda', 'not find method!')

'not find method!'

<b>   6.11.2 hasattr()</b>

   判斷一個對象是否具有屬性或方法。傳回一個布爾值。

&gt;&gt;&gt; hasattr(c, 'name')

true

&gt;&gt;&gt; hasattr(c, 'namea')

false

&gt;&gt;&gt; hasattr(c, 'method')

&gt;&gt;&gt; hasattr(c, 'methoda')

<b>   6.11.3 setattr()</b>

   給對象屬性重新指派或添加。如果屬性不存在則添加,否則重新指派。

&gt;&gt;&gt; hasattr(c, 'age')

&gt;&gt;&gt; setattr(c, 'age', 22)

&gt;&gt;&gt; c.age

22

<b>   6.11.4 delattr()</b>

   删除對象屬性。

&gt;&gt;&gt; delattr(c, 'age')

&gt;&gt;&gt; hasattr(c, 'age')             

<b>6.12 類裝飾器</b>

與函數裝飾器類似,不同的是類要當做函數一樣調用:

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()

('hello world!', 'f1')

<b>6.13 類内置裝飾器</b>

   下面介紹類函數裝飾器,在實際開發中,感覺不是很常用。

<b>   6.10.1 @property</b>

   @property屬性裝飾器的基本功能是把類中的方法當做屬性來通路。

   在沒使用屬性裝飾器時,類方法是這樣被調用的:

...    def __init__(self, a, b):

...      self.a = a

...      self.b = b

...    def func(self):

...      print self.a + self.b

&gt;&gt;&gt; c = a(2,2)

&gt;&gt;&gt; c.func()

4

&gt;&gt;&gt; c.func

&lt;bound method a.func of &lt;__main__.a instance at 0x7f6d962b1878&gt;&gt;

   使用屬性裝飾器就可以像屬性那樣通路了:

...     def __init__(self, a, b):

...       self.a = a

...       self.b = b

...     @property

...     def func(self):

...       print self.a + self.b

  file "&lt;stdin&gt;", line 1, in &lt;module&gt;

typeerror: 'nonetype' object is not callable

<b>   6.10.2 @staticmethod</b>

   @staticmethod是靜态方法裝飾器,可以通過類對象通路,也可以通過執行個體化後類對象執行個體通路。

   執行個體方法的第一個參數是self,表示是該類的一個執行個體,稱為類對象執行個體。

   而使用靜态方法裝飾器,第一個參數就不用傳入執行個體本身(self),那麼這個方法當做類對象,由python自身處理。

   看看普通方法的用法:

&gt;&gt;&gt; class a:                     

...   def staticmethod(self):   

...      print "not static method..."

&gt;&gt;&gt; c = a()         

&gt;&gt;&gt; c.staticmethod()

not static method...

   使用靜态方法則是這麼用:

&gt;&gt;&gt; class a:                   

...   @staticmethod             

...   def staticmethod():       

...     print "static method..."

&gt;&gt;&gt; a.staticmethod()   # 可以通過類調用靜态方法

static method...

&gt;&gt;&gt; c = a()   

&gt;&gt;&gt; c.staticmethod()   # 還可以使用普通方法調用

    靜态方法和普通的非類方法作用一樣,隻不過命名空間是在類裡面,必須通過類來調用。一般與類相關的操作使用靜态方法。

<b>   6.10.3 @classmethod</b>

<b>   </b>@classmethod是類方法裝飾器,與靜态方法裝飾器類似,也可以通過類對象通路。主要差別在于類方法的第一個參數要傳入類對象(cls)。

...   @classmethod             

...   def classmethod(cls):   

...     print "class method..."

...     print cls.__name__

&gt;&gt;&gt; a.classmethod()

class method...

a

<b>6.14 __call__方法</b>

可以讓類中的方法像函數一樣調用。

...   def __call__(self, x): 

...     print "call..."

...     print x

&gt;&gt;&gt; c(123)

call...

123

...   def __call__(self, *args, **kwargs):

...      print args

...      print kwargs

&gt;&gt;&gt; c(1,2,3,a=1,b=2,c=3)

(1, 2, 3)

{'a': 1, 'c': 3, 'b': 2}