Python--(類和對象 下篇)

面向對象的三大特徵:封裝、繼承、多態

封裝:隱藏內部實現 提供外部接口 易於修改,類的內部修改並不影響外部更好地保護類或對象的屬性值的安全

(代碼中:if __name__=='__main__'代表程序主入口是從這裏進入的)

一、封裝(接上篇部分)

<1>類屬性:屬於類的成員,對象共有的

修改方式: 類名.類屬性=.../實例對象.__class__類屬性

        類屬性對象屬性比較使用:

        類屬性可以全部使用,而對象屬性僅限於自己對象的使用

例:

 
class Person:
    def __init__(self):
        self.name='無名氏'
        self.age=18
    def show(self):
        print('姓名:'+self.name+'年齡:',self
              .age)
if '__main__'==__name__:
    Person.phone='110'  #類屬性給參數賦值
    zhangsan=Person()
    zhangsan.name='張三'
    zhangsan.age=20
    zhangsan.gender='男'  #對象屬性 當兩個對象需要區分時使用對象屬性
    zhangsan.show()
    zhangsan.__class__.phone='123456'  #當作爲全局都能夠訪問時用類屬性/給一個類賦值所以下面的李四電話顯示與張三相同
    print('zhangsan.gender'+zhangsan.gender)
    print(zhangsan.__class__.phone)

    lisi = Person()
    lisi.name = '李四'
    lisi.age = 20
    lisi.show()
    # lisi.__class__.phone = '520520'  #取消註釋會顯示電話爲520520
    print(lisi.__class__.phone)

執行命令得

 
姓名:張三年齡: 20
zhangsan.gender男
123456
姓名:李四年齡: 20
123456

<2>類方法:用於拓展原來函數功能的一種函數

在方法上添加@classmethod 裝飾器(類方法更傾向於工具方法,不需要重複創建對象.不是以對象的形式去調用)

       用法:

@classmethod

def class_method(cls):

特點:可以通過類方法調用類屬性,也可以通過對象調用類屬性

 
class Person:
    @classmethod
    def sayHello(cls):
        print('這個是一個Person類')
if '__main__'==__name__:
    Person.sayHello()

執行命令得

這個是一個Person類

<3>靜態方法:

方法前加@staticmethod,(靜態方法可以加參數,靜態方法既和類沒關係,也和對象沒關係,也可以通過類和對象調用)

一般情況下,靜態方法會單獨放在一個模塊裏

例:

 
class Person:
    @staticmethod
    def sayGood(say):#括號裏可填寫可不填寫
        print(say,'GOOD')
if '__main__'==__name__:
    Person.sayGood(123)#上面的括號裏填寫了參數這裏也必須填寫內容否則報錯

執行命令得

 
123 GOOD

二、繼承

1.子類(父類)

概念:子類繼承父類.子類可以使用父類的屬性和方法,簡化代碼

當生成子類對象時,先初始化父類對象.如果父類有__init__()方法,並且有屬性時,要通過子類的構造賦值.一個類可以有多個子類

初始化時,

子類中調用父類的屬性時,在__init__()方法中使用四種方法如下:

<1>super().__init__(屬性)(調用有參)切記不能在屬性前加self 否則報錯

<2>self.屬性=''一般不使用這類形式已在父類中存在

<3>父類.__init__(self,屬性)(調用有參)切記必須在屬性前加self否則報錯

       父類.__init__(self)(調用無參)

<4>super(子類,self).__init__(屬性)

子類中調用父類的方法時的用法:super().父類方法()

例:

 
class Pet:
    def __init__(self,name,love,health):
        '''
        快速初始化自定義數據
        :param name:
        :param love:
        :param health:
        '''
        self.name=name
        self.love=love
        self.health=health
    def show(self):
        print('寵物名'+self.name+' 健康值',self.health,'親密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        # <1>super().__init__(name,love,health)
        # <2>通過self.name='可樂'形式逐個代替
        # <3>Pet.__init__(self,name,love,health)
        super(Dog, self).__init__(name,love,health)
        self.strain='二哈'
    def eat(self):
        print(self.strain+'  正在吃飯')
    def showInfo(self):
        super().show()
        print('品種: ',self.strain)
if __name__=='__main__':
   dog=Dog('可樂 ',100,0)
   dog.eat()
   dog.show()
   dog.showInfo()

執行命令得

 
二哈  正在吃飯
寵物名可樂  健康值 0 親密度: 100
寵物名可樂  健康值 0 親密度: 100
品種:  二哈

練習

交通工具類:屬性:名稱 方法:行駛
子類:卡車,屬性:載重,重寫行駛的方法
子類:火車,屬性:車箱個數,重寫行駛的方法

 
class Traffic:
    def __init__(self,name):
        self.name=name
        self.way='travel'
    def run(self):
        print('交通工具'+self.name+'正在行駛')
class Truck(Traffic):
    def __init__(self,name,weight):
        super(Truck, self).__init__(name)
        self.weight=weight
    def runInfo(self):
        super().run()
        print('卡車'+self.name+'載重'+self.weight+'飛馳')
class Train(Traffic):
    def __init__(self,name,num):
        super(Train, self).__init__(name)
        self.num=num
    def runInfo(self):
        super().run()
        print('火車'+self.name+'有'+str(self.num)+'節車廂')
if '__main__'==__name__:
    kache=Truck('一汽','1噸')
    kache.runInfo()

    huoche=Train('和諧號',15)
    huoche.runInfo()

執行命令得

 
交通工具一汽正在行駛
卡車一汽載重1噸飛馳
交通工具和諧號正在行駛
火車和諧號有15節車廂

總結:

當子類繼承父類時,子類的構造方法應該包含父類和子類共同的屬性,在子類的初始化__init__()方法中,將父類屬性傳遞給父類,子類的屬性賦值給子類(即name爲父類屬性傳遞給父類 weight和num爲自己獨特屬性複製給自己)

方法重寫:

子類繼承父類時,子類的方法簽名和父類一樣,此時子類重寫了父類的方法,當生成子類對象時,調用的格式子類重寫得方法

2.繼承的特性之一:傳遞性

三代繼承:子類初始化方法需要祖父、父類及自己的屬性,可以調用父類的初始化方法傳參,可以重寫父類的方法

構造順序:祖父類-->父類-->自己

 
class Pet:
    def __init__(self,name,love,health):
        self.name=name
        self.love=love
        self.health=health
        print('pet')
    def show(self):
        print('寵物名'+self.name+' 健康值',self.health,'親密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        super(Dog, self).__init__(name,love,health)
        self.strain='泰迪'
        print('dog')
    def eat(self):
        print(self.strain+'  正在吃飯')
    def showInfo(self):
        super().show()
        print('品種: ',self.strain)
class Taidi(Dog):
    def __init__(self,name,love,health):
        super(Taidi, self).__init__(name,love,health)
        print('taidi')
if __name__=='__main__':
   dog=Taidi('可樂 ',100,0)
   dog.eat()
   dog.showInfo()

執行命令得

 
pet
dog
taidi #pet. dog. taidi打印的先後順序代表構造順序
泰迪  正在吃飯
寵物名可樂  健康值 0 親密度: 100
品種:  泰迪

類根源頂級父類-->繼承object

被繼承的類叫:基類 父類

繼承的類叫:子類 派生類

方法重寫:

如果子類重寫的方法想調用父類方法時,在子類方法中寫法:父類.方法(self)或super().父類方法()


3.私有屬性、私有方法均不能在類外面被調用

多繼承:類同時繼成多個父類 如:class C(A,B),當有AB均有相同方法,而子類又重寫時,調用水的方法(子類)如果子類沒有重寫方法,則調用那個父類的方法?(左側,優先繼承得類)

 
class Pet:
    def __init__(self,name,love,health):
        self.name=name
        self.love=love
        self.health=health
    def show(self):
        print('寵物名'+self.name+' 健康值',self.health,'親密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        super(Dog, self).__init__(name,love,health)
        self.strain='哈士奇'
    def showInfo(self):
        super().show()
        print('品種: ',self.strain)
class lang:
    def show(self):
        print('我是一匹來自北方的狼')
class Hashiqi(lang,Dog):#Hashiqi中沒有重寫show時調用左側的lang父類
    def __init__(self,name,love,health):
        super(Hashiqi, self).__init__(name,love,health)
if __name__=='__main__':
   dog=Hashiqi('可樂 ',100,0)
   dog.showInfo()
   dog.show()

執行命令得

 
哈士奇  正在吃飯
寵物名可樂  健康值 0 親密度: 100
品種:  哈士奇
我是一匹來自北方的狼

類名.mro(),可以看到所有父類,及搜索順序
這就是動態語言的"鴨子類型",她並不要求嚴格的繼承體系,一個對象只要"看起來像鴨子,走起路來像鴨子",那他就可以被看做鴨子

練習

(圖書\DVD管理系統)

----DVD管理系統----:
1.查詢所有DVD
2.增加DVD
3.借出DVD
4.歸還DVD
5.退出

 
class DVD:
    def __init__(self,name,price,state):
        self.name=name
        self.price=price
        self.state=state
if '__main__'==__name__:
    xiyouji=DVD('西遊記',100,0)#0代表未借出 1 代表已借出
    sanguo=DVD('三國',100,1)
    shuihuzhuan=DVD('水滸傳',100,0)
    dvds={xiyouji.name:xiyouji,sanguo.name:sanguo,shuihuzhuan.name:shuihuzhuan}
    while True:
        print('1.查詢所有DVD')
        print('2.增加DVD')
        print('3.借出DVD')
        print('4.歸還DVD')
        print('5.退出')
        num=int(input('請輸入數字:'))
        if num==1:
            print('名稱\t價格\t狀態')
            for key in dvds.keys():
                if dvds.get(key).state==0:
                    print(key+' \t'+str(dvds.get(key).price)+' \t'+'未借出')
                else:
                    print(key+' \t'+str(dvds.get(key).price)+' \t'+'已借出')
        elif num==2:
            name=input('請輸入新的DVD:')
            while name in dvds.keys():
                name=input('此DVD已存在 請重新輸入名字:')
            price=int(input('請輸入新DVD價格:'))
            new_name=DVD(name,price,0)
            dvds[name]=new_name
            print('增加成功')
        elif num==3:
            name = input('請輸入你所需要的DVD:')
            while name not in dvds.keys():
                name=input('此DVD不存在 請重新輸入名字:')

            if dvds.get(name).state==1:
                print(name,'已借出')
            else:
                dvds.get(name).state=1
                print('借出成功')
        elif num==4:
            name = input('請輸入你所歸還的DVD:')
            while name not in dvds.keys():
                name=input('此DVD不存在,請重新輸入DVD名字')
            if dvds.get(name).state==0:
                print('此DVD未借出')
            else:
                dvds.get(name).state=0
                date=int(input('請輸入借書日期'))
                print('掃一掃付款',int(date*int(dvds.get(name).price)),'元')
                print('歸還成功')
        elif num==5:
            print('歡迎使用')
            break

4.__new__(cls):#用來創建對象,而且必須有返回值

return object.__new__(cls);

#return super(子類,cls).__new__(cls,args,kwargs)

可以用id(cls)看地址

當有屬性時,需要在__new__()中也添加屬性

單例模式: 該模式的主要目的是確保某一個類只有一個實例存在(實例等同於一個對象等同於一個內存地址等同於一塊空間)

應用:資源共享

 
class singleton:
    __instace=None
    def __new__(cls, *args, **kwargs):
        print('__new__')
        if cls.__instace==None: #說明沒有創建過
            cls.__instace=object.__new__(cls)
            return cls.__instace
        else:#創建過
            return cls.__instace
    def __init__(self):
        print('__init__')
s1=singleton()
s2=singleton()
s3=singleton()
print(id(s1))
print(id(s2))
print(id(s3))

執行命令得

 
__new__
__init__
__new__
__init__
__new__
__init__
77853488
77853488
77853488

三、多態

多態:多種形態 .不同的類對同一個消息/動作做出不同的解釋執行不一樣的代碼

多態的3個必要條件:1.發生繼承關係2.子類重寫父類方法3.里氏代換原則

使用方式:以父類類型作爲形參

<1>工廠類:在一個類中生成很多對象,簡單工廠模式(Simple Factory Pattern)

          是通過專門定義一個類來負責創建其他類的實例,被創建的事例同牀都具有共同得父類 ,並且重寫父類方法.

四則運算的父類,接受用戶輸入得數值 Operation 

OperationAdd  

OperationSub

 OperationMul 

OperationDiv 

OperationFactroy(object)

 
# 四則運算得父類 ,接受用戶輸入的數值Operation
class Operation:#四則運算父類
    def __init__(self,num1,num2):
        self.num1=num1
        self.num2=num2
    def yunsuan(self):
        pass
class OperationAdd(Operation):
    def __init__(self,num1,num2):
        super(OperationAdd, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 + self.num2
class OperationSub(Operation):
    def __init__(self,num1,num2):
        super(OperationSub, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 - self.num2
class OperationMul(Operation):
    def __init__(self,num1,num2):
        super(OperationMul, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 * self.num2
class OperationDiv(Operation):
    def __init__(self,num1,num2):
        super(OperationDiv, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 // self.num2
#工廠類  需要加減乘除時 工廠類來進行創建
class OperationFactory(object):
    @classmethod#添加修飾器 類名.方法即可調用
    def getOperation(self,sign,num1,num2):#獲取運算符算的方法
        if '+'.__eq__(sign):
            return OperationAdd(num1,num2)
        if '-'.__eq__(sign):
            return OperationSub(num1,num2)
        if '*'.__eq__(sign):
            return OperationMul(num1,num2)
        if '/'.__eq__(sign):
            return OperationDiv(num1,num2)
if '__main__'==__name__:
    num1=int(input('請輸入第一個操作數:'))

    num2 = int(input('請輸入第二個操作數:'))
    sign = input('請輸入操作符:')
    #返回與操作符對應的 運算對象
    Operation=OperationFactory.getOperation(sign,num1,num2)
    result=Operation.yunsuan()
    print('運算結果:',result)

<2>isinstance()函數 判斷是否繼承關係或者某一個變量是否是某一個數據類型

 
#連接上面的加減乘除爲例
'''
'''
if '__main__'==__name__:
    num1=int(input('請輸入第一個操作數:'))

    num2 = int(input('請輸入第二個操作數:'))
    sign = input('請輸入操作符:')
    #返回與操作符對應的 運算對象
    Oper=OperationFactory.getOperation(sign,num1,num2)
    result=Oper.yunsuan()
    print('運算結果:',result)
    # 判斷是否是Oper得數據類型
    if isinstance(Oper,OperationAdd):
        print('創建的是OperationAdd 類型的對象')
    if isinstance(Oper,OperationSub):
        print('創建的是OperationSub 類型的對象')
    if isinstance(Oper,OperationMul):
        print('創建的是OperationMul 類型的對象')
    if isinstance(Oper,OperationDiv):
        print('創建的是OperationDiv 類型的對象')
執行命令得
 
創建的是OperationAdd 類型的對象
創建的是OperationSub 類型的對象
創建的是OperationMul 類型的對象
創建的是OperationDiv 類型的對象

判斷是否繼承關係

 
'''
'''
    print(isinstance(Oper,Operation))
    print(isinstance(Oper,object))

執行命令得

 
True
True


<3>獲取類名,對象名,屬性名

getattr(類名/對象名)  setattr(類名/對象名)  

hasattr(類名/對象名,'屬性名')只能判斷不能獲取

用法

 
'''
'''
    #獲取屬性值
    Oper=OperationFactory.getOperation('+',1,5)
    print(Oper.__getattribute__('num2'))
    #當所要的是屬性值沒有時
    Oper.__setattr__('num3',7)
    print(Oper.__getattribute__('num3'))
    print(hasattr(Oper,'num3'))

執行命令得

 
5
7
True

<4>動態語言

爲類動態添加函數 所有的對象都能用

用法:

 
#以加減乘除爲例
class Operation:'''
class OperationAdd(Operation):'''
class OperationSub(Operation):'''
class OperationMul(Operation):'''
class OperationDiv(Operation):'''
class OperationFactory(object):
def setName(self,name):#動態添加函數(方法)
    self.name=name
if '__main__'==__name__:
    Oper=OperationFactory.getOperation('+',1,5)
    Oper2=OperationFactory.getOperation('-',10,5)
    Operation.setName=setName
    Oper.setName('加法')
    print('運算方法:'+Oper.name)
    Oper2.setName('減法')
    print('運算方法:'+Oper2.name)

執行命令得

 
運算方法:加法
運算方法:減法

<5>__call__直接在實例本身上調用

    通過callable()函數,我們就可以判斷一個對象是否是可調用的對象

 
class Person:
    def __call__(self, *args, **kwargs):
        print('執行了call函數')
if '__main__'.__eq__(__name__):
    zhangsan=Person()
    zhangsan()# 或者填寫print(callable(zhangsan))

執行命令得

 
執行了call函數#True

<6>@property得使用

@property 註解優化getter  setter-->@函數者.setter

@property裝飾器是負責把一個方法變成屬性調用,它的實現比較複雜,把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身創建了另一個裝飾器@xx.setter,負責把一個setter方法變成屬性賦值,於是,我們就擁有一個可控的屬性操作

 
class Person:
    @property
    def Age(self):
        return self.age
    @Age.setter
    def Age(self,age):
        if age<0 or age>100:
            self.age=18
        else:
            self.age=age
            print('賦值了',self.age)
if '__main__'.__eq__(__name__):
     zhangsan=Person()
     zhangsan.Age=30
     print(zhangsan.Age)

執行命令得

 
賦值了 30
30

<7>動態語言的靈活性,爲了達到限制的目的,Python允許在定義class得時候定義一個特殊的__slots__變量.來限制該class實例能添加的屬性

 
class student:
    __slots__ = ('name','age')
stu=student()
stu.name='zhangsan'
stu.score=99

執行命令得

 
#報錯AttributeError: 'student' object has no attribute 'score'

四、里氏替換原則

1.子類可以實現父類的抽象方法,但是不能覆蓋父類的非抽象方法

2.子類中可以增加自己特有方法

3.當子類覆蓋或實現父類得方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數更寬鬆

4.當子類的方法實現父類得抽象方法時,方法的後置條件(及方法的返回值)要比父類更嚴格

始終記住里氏替換原則的最終目的就是保持父類的方法不被覆蓋




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章