python基礎(25):面向對象三大特性二(多態、封裝)

1. 多態

1.1 什麼是多態

多態指的是一類事物有多種形態。python

動物有多種形態:人,狗,豬。安全

import abc
class Animal(metaclass=abc.ABCMeta): #同一類事物:動物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #動物的形態之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #動物的形態之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #動物的形態之三:豬
    def talk(self):
        print('say aoao')

文件有多種形態:文本文件,可執行文件。函數

import abc
class File(metaclass=abc.ABCMeta): #同一類事物:文件
    @abc.abstractmethod
    def click(self):
        pass

class Text(File): #文件的形態之一:文本文件
    def click(self):
        print('open file')

class ExeFile(File): #文件的形態之二:可執行文件
    def click(self):
        print('execute file')

1.2 多態性

什麼是多態動態綁定(在繼承的背景下使用時,有時也稱爲多態性)。spa

多態性是指在不考慮實例類型的狀況下使用實例:設計

在面向對象方法中通常是這樣表述多態性:
向不一樣的對象發送同一條消息(!!!obj.func():是調用了obj的方法func,又稱爲向obj發送了一條消息func),不一樣的對象在接收時會產生不一樣的行爲(即方法)。
也就是說,每一個對象能夠用本身的方式去響應共同的消息。所謂消息,就是調用函數,不一樣的行爲就是指不一樣的實現,即執行不一樣的函數。code

好比:老師.下課鈴響了(),學生.下課鈴響了(),老師執行的是下班操做,學生執行的是放學操做,雖然兩者消息同樣,可是執行的效果不一樣。對象

peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是動物,只要是動物確定有talk方法
#因而咱們能夠不用考慮它們三者的具體是什麼類型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更進一步,咱們能夠定義一個統一的接口來使用
def func(obj):
    obj.talk()

2. 封裝

2.1 什麼叫封裝

隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。blog

好處:繼承

1. 將變化隔離; 接口

2. 便於使用;

3. 提升複用性; 

4. 提升安全性。

封裝原則:

1. 將不須要對外提供的內容都隱藏起來;

2. 把屬性都隱藏,提供公共方法對其訪問。

2.2 私有變量和私有方法

在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)

2.2.1 私有變量

#其實這僅僅這是一種變形操做
#類中全部雙下劃線開頭的名稱如__x都會自動變造成:_類名__x的形式:

class A:
    __N=0 #類的數據屬性就應該是共享的,可是語法上是能夠把類的數據屬性設置成私有的如__N,會變形爲_A__N
    def __init__(self):
        self.__X=10 #變形爲self._A__X
    def __foo(self): #變形爲_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在類內部才能夠經過__foo的形式訪問到.

#A._A__N是能夠訪問到的,即這種操做並非嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形

這種自動變形的特色:

1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。

2.這種變形其實正是針對外部的變形,在外部是沒法經過__x這個名字訪問到的。

3.在子類定義的__x不會覆蓋在父類定義的__x,由於子類中變造成了:_子類名__x,而父類中變造成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是沒法覆蓋的。

這種變形須要注意的問題是:

1.這種機制也並無真正意義上限制咱們從外部直接訪問屬性,知道了類名和屬性名就能夠拼出名字:_類名__屬性,而後就能夠訪問了,如a._A__N

2.變形的過程只在類的內部生效,在定義後的賦值操做,不會變形

3.在繼承中,父類若是不想讓子類覆蓋本身的方法,能夠將方法定義爲私有的

2.2.2 私有方法

#正常狀況
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定義成私有的,即__fa
>>> class A:
...     def __fa(self): #在定義時就變形爲_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只會與本身所在的類爲準,即調用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A

2.3 封裝與擴展性

封裝在於明確區份內外,使得類實現者能夠修改封裝內的東西而不影響外部調用者的代碼;而外部使用用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。這就提供一個良好的合做基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足爲慮。

#類的設計者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏了內部的實現細節,此時咱們想求的是面積
        return self.__width * self.__length

#使用者
>>> r1=Room('臥室','egon',20,20,20)
>>> r1.tell_area() #使用者調用接口tell_area

#類的設計者,輕鬆的擴展了功能,而類的使用者徹底不須要改變本身的代碼
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏內部實現,此時咱們想求的是體積,內部邏輯變了,只需求修該下列一行就能夠很簡答的實現,並且外部調用感知不到,仍然使用該方法,可是功能已經變了
        return self.__width * self.__length * self.__high

#對於仍然在使用tell_area接口的人來講,根本無需改動本身的代碼,就能夠用上新功能
>>> r1.tell_area()

2.4 property屬性

什麼是特性property?

將一個類的函數定義成特性之後,對象再去使用的時候obj.name,根本沒法察覺本身的name是執行了一個函數而後計算出來的,這種特性的使用方式遵循了統一訪問的原則。

除此以外,看下:

ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是「兒子」,但我不知道爲何你們 不說「女兒」,就像「parent」原本是「父母」的意思,但中文都是叫「父類」)公開
【private】
這種封裝對誰都不公開

python並無在語法上把它們三個內建到本身的class機制中,在C++裏通常會將全部的全部的數據都設置爲私有的,而後提供set和get方法(接口)去設置和獲取,在python中經過property方法能夠實現。

class Foo:
    def __init__(self,val):
        self.__NAME=val #將全部的數據屬性都隱藏起來

    @property
    def name(self):
        return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置)

    @name.setter
    def name(self,value):
        if not isinstance(value,str):  #在設定值以前進行類型檢查
            raise TypeError('%s must be str' %value)
        self.__NAME=value #經過類型檢查後,將值value存放到真實的位置self.__NAME

    @name.deleter
    def name(self):
        raise TypeError('Can not delete')

f=Foo('egon')
print(f.name)
# f.name=10 #拋出異常'TypeError: 10 must be str'
del f.name #拋出異常'TypeError: Can not delete'

一個靜態屬性property本質就是實現了get,set,delete三種方法。

class Foo:
    @property
    def AAA(self):
        print('get的時候運行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的時候運行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的時候運行我啊')

#只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
class Foo:
    def get_AAA(self):
        print('get的時候運行我啊')

    def set_AAA(self,value):
        print('set的時候運行我啊')

    def delete_AAA(self):
        print('delete的時候運行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

怎麼用?

class Goods:

    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.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
print(obj.price)
del obj.price     # 刪除商品原價

2.5 classmethod

class Classmethod_Demo():
    role = 'dog'

    @classmethod
    def func(cls):
        print(cls.role)

Classmethod_Demo.func()

2.6 staticmethod

class Staticmethod_Demo():
    role = 'dog'

    @staticmethod
    def func():
        print("當普通方法用")

Staticmethod_Demo.func()
相關文章
相關標籤/搜索