天天看點

VII python面向對象

以下概念及舉例均在python3.*中實作;

1、

面向過程&面向對象:

op

oo

産生背景

科學計算為目标的必然産物

軟體應用領域的擴張和系統膨脹之後應運而生

程式設計方法

自頂向下

自底向上

代碼主體結構

程式=資料(變量)+算法(函數|過程)

程式=對象+互動

資料操作主體

由函數|過程進行加工與展現

在對象的方法中加工與展現

模拟方法

通過函數|過程操縱表現世界的資料與狀态

把世界描繪成具有主動性的對象之間互動

程式設計思維

搞清處理資料的步驟

面向對象分析

運作效率

較高

較低

雞兔同籠:

先假設全部為雞,計算出腿數;

算出腿數量差;

得出雞數量;

計算另一動物的數量;

注:

自猜想的資料類型;

變量名為大寫就認為是常量;

大小寫敏感;

2、

對象程式設計體驗:

自動運作小遊戲模拟:

在一維的地圖上,有一隻蟲子和一隻螞蟻,每一次它們都走過一個-3,-2,2,3随機機關的距離(標明走法),若達到地圖邊界則放棄移動,當螞蟻蟲子處于同一位置時,螞蟻吃掉蟲子,程式結束;

op:

蟲子的初始位置;

螞蟻的初始位置;

進入循環(條件為螞蟻和蟲子不在同一位置);

依照規則,螞蟻和蟲子移動位置;

直到螞蟻和蟲子走到同一位置,程式結束;

oo:

遊戲中的對象有:地圖、蟲子、螞蟻;

地圖是一維的,隻需要記錄蟲子和螞蟻的位置;

螞蟻和蟲子知道自己的位置;

螞蟻和蟲子能按規則移動;

定義地圖、螞蟻、蟲子三個類;

主程式中執行個體化它們,并通過對象間的互動來完成遊戲的模拟;

3、

面向對象入門:

了解對象:

對象可以指自然界中的任何事物;

計算機為解決某個領域問題所使用的事物(自然界中的事物的模型化);

事物(對象)隻有自身的特征或能力;

計算機中的對象具有解決問題所需的特征或能力;

對象優越性:

封裝(将模型的特征和能力打包在一起;模型的改變由模型自身來完成,就像自然界的事物一樣;隐藏模型的細節,外界隻能使用它,而不必(不能)改變它);

繼承(符合自然界的分類規律;快速的代碼重用);

多态(子類可以繼承父類的特征與能力,還可通過自定義來修改其特征與能力;duck typing鴨子類型);

組合(一個模型可以由其它的模型組成);

duck typing來源于james whitcomb riley提出的鴨子測試:

當看到一隻鳥走起來像鴨子,叫起來像鴨子,遊泳起來像鴨子,那麼這隻鳥就可被稱為鴨子;

在鴨子類型中,關注的不是對象的類型本身,而是它是如何使用的;

在有的語言中必須用接口來實作;

4、

定義和使用類:

最簡類定義;

類執行個體化;

類與執行個體之間的關系(

定義類就是建立模型;

執行個體化就是建立真實事物;

例如:模具、印章);

有特征和能力的類(

特征|屬性,是類自身包含或知道的資料;

能力,以方法展現,是類具有能動性的展現);

執行個體化步驟(

調用__new__()方法建立執行個體,__new__()方法自動從object繼承;

調用__init__()方法對其初始化,__init__()方法在類中定義);

添加類說明docstring(

緊跟在類名之後,以三引号包圍的字元串;

檢視類說明,類名.__doc__或help(類名));

新式類與經典類(

python2.*版本,預設是經典類,繼承object為新式類;

python3.*版本,統一為新式類,不用繼承object;

差別:經典類繼承為深度優先;新式類繼承為廣度優先)

描述對象的特征(執行個體屬性;類屬性;私有屬性;特殊屬性):

執行個體屬性:

類被執行個體化以後才具有的屬性;

一般在__init__()方法中建立并初始化;

直接使用即定義,self.屬性名;

引用方法,self.屬性名;

self用來代表類的執行個體;

類外用執行個體名.屬性名方式定義和引用;

相同類的不同執行個體,其執行個體屬性是不相關的;

一般不建議在__init__()方法之外建立和初始化執行個體屬性;

一般不推薦類外定義和修改,修改可以單獨定義方法;

In [13]: class TestClass:

   ....:     def __init__(self):

   ....:         self.a = 0

   ....:         self.b = 10

   ....:     def info(self):

   ....:         print 'a:',self.a,'b:',self.b

   ....:     def define_c(self):

   ....:         self.c = 20

In [14]: tc=TestClass()

In [15]: tc.info()

a: 0 b: 10

In [16]: tc.color='red'   #類外用

In [17]: print tc.color

red

In [18]: tca = TestClass()

In [19]: tcb = TestClass()

In [20]: tca.a = 100

In [21]: tcb.a = 200

In [22]: tca.info()

a: 100 b: 10

In [23]: tcb.info()

a: 200 b: 10

In [24]: tc = TestClass()

In [25]: tc.define_c()

In [26]: print tc.c

20

類屬性:

類定義後就存在,而且不需要執行個體化;

類屬性使得相同類的不同執行個體共同持有相同變量;

In [27]: class TestCss:

   ....:     cssa = 'class-attribute'

   ....:         print 'a:',self.a,'b:',self.b,TestCss.cssa

In [29]: tc = TestCss()

In [30]: tc.info()

a: 0 b: 10 class-attribute

In [31]: tca = TestCss()

In [33]: tca.info()

In [34]: TestCss.cssa = 0   #類外修改其屬性

In [35]: tc.info()

a: 0 b: 10 0

In [36]: tca.info()

私有屬性:

不提供限制屬性通路的關鍵字(無法限制類的各種屬性在類外直接通路);

使用__開頭的變量名加以标示,隻有類對象自己能通路(限制性的);

使用_開頭的變量名加以标示,隻有類對象及其子類能通路(非強制性),對程式員來說,是标示性的而非限制性的,在類外還可以修改和檢視;

In [37]: class TestPri:

   ....:         self.__a = 0   #在此處初始化時執行個體屬性就被隐藏了

   ....:         print self.__a

   ....:        

In [38]: a = TestPri()

In [39]: a.info()

In [40]: a.__a = 3   #私有屬性不能在外部進行修改,此處相當于重新定義了一個執行個體屬性,與類中定義的self.__a = 0是不一樣的

In [41]: a.info()

In [43]: print a.__a

3

In [44]: class TestPri:

   ....:         self._a = 10

   ....:         print self._a

In [45]: a = TestPri()

In [46]: a.info()

10

In [47]: a._a = 30   #在類外可修改和檢視

In [48]: a.info()

30

In [49]: print a._a

特殊屬性:

__doc__

__name__   #類名稱

__dict__   #執行個體屬性的所有屬性名及值組成的dictionary

__module__   #該類所在的子產品名

__base__   #該類的父類

5、讓對象具有能動性:

類的方法的定義:

def fun_name(self,…):

         pass

其中,self表示類的執行個體,在調用方法時由系統自動提供;

方法定義時必須指明self參數;

類的方法的調用:

與普通函數調用類似,即使參數清單為空也不能省略();

在類的内部調用,self.方法名(參數清單);

在類的外部調用,執行個體名.方法名(參數清單);

以上兩種調用方法,提供的參數清單中都不用包括self;

類内方法互相調用:

在一個類的内部,方法之間是可以互相調用的;

調用方法,self.方法名(參數清單);

構造方法及其作用:

構造方法即使用__init__()方法;

構造方法的作用是在類執行個體化時初始化執行個體;

__init__()方法就是類執行個體化的第二步自動調用的函數,其方法名是固定的,但其參數同普通方法一樣,至少要帶self參數;

初始化執行個體包括,定義和初始化執行個體屬性,或調用類的一些方法;

構造方法可帶有除self外的其它各種參數,關鍵字參數、預設參數、用tuple收集參數、用字典收集關鍵字參數等,可以達到在執行個體化類時,為相應的屬性傳入指定的值;

定義類的執行個體方法,和執行個體屬性一樣,必須進行類執行個體化之後,才能存在和調用它們;

python3.*版本,print是函數;python2.*,print是語句;

6、深入類的屬性:

同名的類屬性與執行個體屬性:

執行個體名.屬性名,優先引用執行個體屬性;

類名.屬性名,隻能引用類屬性;

屬性通路的特殊方法(反射,或叫自省):

用字元串來操作類的屬性|方法的方式;

主要工具函數(在編寫架構或特殊項目時才用到):

hasattr(obj_name,'屬性名')

setattr(obj_name,'屬性名',值)

getattr(obj_name,'屬性名')

In [50]: class TestClass:

   ....:     a = 0

   ....:         self.a = 10

   ....:         self.b = 20

In [51]: a = TestClass()

In [52]: a.a

Out[52]: 10

In [53]: a.b

Out[53]: 20

In [54]: TestClass.a

Out[54]: 0

In [55]: TestClass.b   #抛異常,此類未定義b屬性

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-55-39cab4ae7dc4> in <module>()

----> 1 TestClass.b

AttributeError: class TestClass has no attribute 'b'

In [56]: getattr(a,'a')   #擷取執行個體a的a屬性

Out[56]: 10

In [57]: setattr(a,'a',20)

In [58]: a.a

Out[58]: 20

In [59]: hasattr(a,'b')

Out[59]: True

屬性包裝:

将方法包裝成屬性,以隐藏相關實作,使用者使用時用屬性的方式來用;

控制屬性的類型和範圍,如某屬性隻能是正數且在0-100之間;

虛拟屬性(由其它屬性處理後得來);

三種屬性操作(3個裝飾器):

@property   #可讀

@<property-name>.setter   #可寫

@<property-name>.deleter   #可删

舉例:

attribute_package.py

#!/usr/bin/env python3.6

#

class Washer:

    def __init__(self,water=100,scour=2):

        self._water = water

        self.scour = scour

        self.year = 2010

    @property

    def water(self):

        return self._water

    @water.setter

    def water(self,water):

        if 0 < water <= 500:

            self._water = water

        else:

            print('set failure')

    def total_year(self):

        return 2018 - self.year

if __name__ == '__main__':

    w = Washer()

    print(w.water)

    w.water = -123

    print(w.total_year)

描述符:

将實作特殊協定方法的類作為另一個類的類屬性;

用來攔截和控制屬性通路并可重複使用;

協定方法:

__get __()

__set__()

__delete__()

分類:

資料描述符(實作全部協定方法);

非資料描述符(實作部分協定方法);

說明:

所有類成員函數都是非資料描述符;

同名的執行個體屬性和非資料描述符(以方法為例)通路優先級;

描述符隻能在新式類中使用;

__call__(),讓類的執行個體如函數一樣可調用;

舉例(資料描述符):

data_description.py

class NonNeg:

    def __init__(self,default=0):

        self.default = default

    def __get__(self,instance,owner):

        return self.default

    def __set__(self,instance,val):

        if val > 0:

            self.default = val

            print('the value must be nonnegative')

    def __delete__(self,instance):

        pass

class Movie:

    rating = NonNeg()

    score = NonNeg()

    m = Movie()

    print('rating:',m.rating)

    print('score:',m.score)

    m.rating = 80

    m.score = 30

舉例(非資料描述符):

In [1]: class Test:

   ...:     def pr(self):

   ...:         print('test')

   ...:        

In [2]: t = Test()

In [3]: dir(t.pr)   #pr方法實作了__get__()方法

Out[3]:

['__call__',

 '__class__',

 '__delattr__',

 '__dir__',

 '__doc__',

 '__eq__',

 '__format__',

 '__func__',

 '__ge__',

 '__get__',

 '__getattribute__',

In [4]: t.pr = 10   #執行個體屬性和非資料描述符同名,優先級為先執行個體屬性再非資料描述符,此例中t.pr的屬性掩蓋了pr()方法

In [5]: t.pr

Out[5]: 10

In [6]: del t.pr

In [7]: t.pr

Out[7]: <bound method Test.pr of <__main__.Test object at 0x7f10ecd1c1d0>>

In [8]: t.pr()

test

7、

類方法、靜态方法:

靜态方法:

定義:

用staticmethod裝飾;

參數不用self;

通路特性:

不能引用或通路執行個體屬性;

可通過類、類變量通路類屬性;

調用方式:

可用類、類執行個體調用;

本質:

在類中的一個普通函數;

使面向對象程式中的函數歸屬于類,易于代碼管理;

用法:

與類相關,但不依賴或改變類與執行個體;

建立不同的執行個體;

把類相關工具方法放入類中;

static_method.py

    company = 'panasonic'

    def __init__(self,water=10,scour=2):

        self.water = water

    @staticmethod

    def spins_ml(spins):

        print('company:',Washer.company)

        #print('year:',self.year)   #報錯,不能引用執行個體屬性

        return spins * 0.4

    print(Washer.spins_ml(8))

    print(w.spins_ml(9))

    w = Washer(200,Washer.spins_ml(10))

    print(w.company)

    print(w.year)

    print(w.scour)

    print(w.spins_ml(10))

類方法:

@classmethod;

必須提供參數cls;

通路特性:不能引用或通路執行個體屬性;

調用方法:可以用類、類執行個體調用;

繼承特性:傳入的類變量cls是子類,而非父類;

用途:

與類相關,但不依賴或改變類的執行個體;

工廠方法,建立類執行個體,完成有關預處理;

在類内調用靜态方法時不用寫死類名;

class_method.py

        return spins * 0.4

    @classmethod

    def get_washer(cls,water,scour):

        #print('year:',self.year)

        return cls(water,cls.spins_ml(scour))

    def start_wash(self):

        print(self.water)

        print(self.scour)

        print('start wash')

    w = Washer.get_washer(100,8)

    w.start_wash()

8、

類的繼承與方法重載:

繼承特點:

oo程式設計的優點之一;

減少代碼和靈活制定新類;

子類具有父類的屬性和方法;

子類不能繼承父類的私有屬性|方法;

子類可添加新的方法;

子類可修改父類的方法;

繼承文法:

定義類時,在類名後寫(繼承的類名);

多重繼承時,括号中放多個父類名;

例:

class MyClass(BaseClass):

重載的文法:

直接定義和父類同名的方法;

修改父類的方法:

在重載的方法中調用父類方法;

同時添加相應的業務邏輯;

多重繼承時如何調用父類方法;

模拟遊戲解析:

sprite,ant,worm

例(編寫一個色子類):

1、具有6個面,每個面為一種顔色;

2、每種顔色代表一種數值1-6;

3、實作一個通過顔色計算兩種其代表數值和的靜态方法;

4、實作一個類方法gen_dice,用于産生這個類的執行個體;

In [2]: class A:

   ...:     pass

   ...:

In [3]: A.__base__   #類A的父類是object,object是python中的新式類

Out[3]: object

In [4]: class B(A):

In [5]: B.__base__   #類B的父類是類A

Out[5]: __main__.A

In [6]: class C:

In [7]: class D(A,C):

In [8]: D.__bases__   #D的所有父類

Out[8]: (__main__.A, __main__.C)

In [9]: class A:

   ...:     def foo(self):

   ...:         print('A foo')

In [10]: class B:

   ....:     def foo(self):

   ....:         print('B foo')

In [11]: class C(A,B):

   ....:     pass

   ....:

In [12]: c = C()

In [13]: c.foo()   #輸出先繼承的類的方法

A foo

In [14]: class D(B,A):

In [15]: D().foo()

B foo

wash.py

9、

類的特殊方法:

深入了解類:

類也是一個對象,但具有建立其自身執行個體的能力;

類可以和一個變量進行綁定;

可以為類增加屬性;

可把類作為函數的參數傳遞;

元類:

類的建立和管理者type;

所有的類都是元類type的執行個體(python3.*);

類執行個體化過程;

In [16]: class Empty:

In [17]: ept = Empty

In [18]: ept

Out[18]: __main__.Empty

In [19]: ept.__base__

Out[19]: object

In [20]: ept()

Out[20]: <__main__.Empty at 0x7fd0adf7c470>

In [21]: ept.foo='foo'

In [22]: ept.foo

Out[22]: 'foo'

In [23]: def use_class(mc):

   ....:     return mc()

In [25]: use_class(Empty)

Out[25]: <__main__.Empty at 0x7fd0adf7ef28>

In [26]: type(Empty)

Out[26]: type

In [27]: Hello = type('Hello',(object,),dict(helo=lambda lf:print('hello')))

In [28]: h = Hello()

In [29]: h.helo()

hello

10、

鴨子類型與多态:

多态:

一種類型具有多種類型的能力;

允許不同的對象對同一消息作出靈活的反應;

以一種通用的方式對待可使用的對象;

非動态語言必須通過繼承和接口來實作;

python中的多态:

通過繼承實作多态(子類可作為父類使用);

子類可重載父類的方法實作多态;

In [31]: class Animal:

   ....:     def move(self):

   ....:         print('Animal is moving...')

In [32]: class Dog(Animal):

In [33]: def move(obj):

   ....:     obj.move()

   ....:    

In [34]: a = Animal()

In [35]: move(a)

Animal is moving...

In [36]: d = Dog()

In [37]: move(d)

In [38]: class Cat(Animal):

   ....:         print('Cat is moving...')

In [41]: class Sheep(Animal):

   ....:         print('Sheep is moving...')

In [42]: move(Cat())

Cat is moving...

In [43]: move(Sheep())

Sheep is moving...

In [44]: move(7)   #報錯

動态語言與鴨子類型:

變量綁定的類型具有不确定性;

函數和方法可接受任何類型的參數;

調用方法時不檢查提供的參數類型;

調用時是否成功由參數的方法和屬性确定;

調用不成功則抛出錯誤;

python中不用定義接口;

In [48]: def test(foo):

   ....:     print(type(foo))

In [49]: test(3)

<class 'int'>

In [50]: test(3.1)

<class 'float'>

In [51]: type(3)

Out[51]: int

In [52]: type(3.1)

Out[52]: float

多态的好處:

可實作開放的擴充與修改的封閉;

使python更具靈活性;

11、

python與設計模式:

設計模式:

用來提高代碼複用和可維護性的方式;

能夠很好的指導軟體設計過程;

是成功的軟體設計模式和體系結構;

用于解決特定類型問題的面向對象程式設計模型;

由于語言的特性不同,設計模式的實作方式和實作難度也會不同;

有的模式已經在語言中内置了,如疊代器模式;

單例模式可直接用子產品級變量來實作;

普通工廠模式可直接通過傳入“類名”作為參數實作;

政策模式:

讓一個對象的某個方法可以随時改變,而不用更改對象的代碼;

對于動态類型的python語言,不需要定義接口;

基本的實作方法,用類作為參數傳遞;

最簡單的執行個體方法,函數作為參數傳遞;

例(單例模式):

single_class.py

class singleClass:

    def __init__(self,x=0):

        self.x = 0

sc = singleClass()

def tsc():

    print(sc.x)

    sc.x = 10

def tsc2():

    sc.x = 9

    tsc()

    tsc2()

例(實作單執行個體的方式):

singleton.py

class Singleton:

    def __new__(cls,*args,**kwargs):

        if not hasattr(cls,'_sgl'):

            cls._sgl = super().__new__(cls,*args,**kwargs)

        return cls._sgl

    sa = Singleton()

    sb = Singleton()

    print(id(sa))

    print(id(sb))

例(普通工廠模式):

normal_factory.py

class Ab:

    a = 3

class Ac:

    a = 0

class MyFactory:

    def get_instance(self,ins):

        return ins()

    mf = MyFactory()

    print(type(mf.get_instance(Ab)))

例(政策模式——用類作為參數傳遞):

class Moveable:

    def move(self):

        print('Move...')

class MoveOnFeet(Moveable):

        print('Move on feet...')

class MoveOnWheel(Moveable):

        print('Move on wheel...')

class MoveObj:

    def set_move(self,moveable):

        self.moveable = moveable()

        self.moveable.move()

class Test:

        print('i am fly')

    m = MoveObj()

    m.set_move(Moveable)

    m.move()

    m.set_move(MoveOnFeet)

    m.set_move(MoveOnWheel)

    m.set_move(Test)

例(政策模式——用函數作為參數傳遞):

def movea():

    print('move a')

def moveb():

    print('move b')

        self.moveable = moveable

        self.moveable()

    m.set_move(movea)

    m.set_move(moveb)

12、

裝飾模式:

一般,通過繼承可獲得父類的屬性,還可通過重載修改其方法;

裝飾模式可不以繼承的方式而傳回一個被修改的類;

類裝飾器;

decorator.py

class BeDeco:

    def be_edit_fun(self):

        print('source fun')

    def be_keep_fun(self):

        print('keep fun')

class Decorator:

    def __init__(self,dec):

        self._dec = dec()

        print('start...')

        self._dec.be_edit_fun()

        self._dec.be_keep_fun()

    bd = BeDeco()

    bd.be_edit_fun()

    bd.be_keep_fun()

    dr = Decorator(BeDeco)

    dr.be_edit_fun()

    dr.be_keep_fun()

def deco(a_class):

    class NewClass:

        def __init__(self,age,color):

            self.wrapped = a_class(age)

            self.color = color

        def display(self):

            print(self.color)

            print(self.wrapped.age)

    return NewClass

@deco

class Cat:

    def __init__(self,age):

        self.age = age

    def display(self):

        print(self.age)

    c = Cat(12,'black')

    c.display()

13、

通過組合來建構複雜的對象:

組合:

類似用各種零件組裝機器(零件——基礎類,機器——包含其它類的類);

展現自下而上的面向對象程式設計方法;

雪人案例:

面向對象分析;

抽象出其類;

實作基類(shape不帶角度參數的圖形元素;shapeAngles需要角度參數的圖形元素);

實作子類(HatTop,HatBotton,Face,Sense,BodyOutline,Button);

組裝子類1(Hat,Head,Body,Shape);

組裝子類2(snow);

本文轉自 chaijowin 51CTO部落格,原文連結:http://blog.51cto.com/jowin/2055034,如需轉載請自行聯系原作者