day15-面向對象基礎(二)

今天整理類的組合以及類的三大特性python

1.類的組合程序員

2.類的繼承算法

3.類的封裝python3.x

4.類的多態app

 

開始今日份整理函數

1.類的組合測試

類與類之間,並非獨立的,不少的時候在正常使用的時候都是類與類之間互相調用,因此就須要對類與類之間的關聯或者是聯繫進行整理一下,就拿以前的英雄聯盟的遊戲人物之間的關係舉例。ui

  1.1類的組合spa

在定義lol中不一樣的英雄人物,那麼正常遊戲過程當中,是須要使用武器等,這個時候就用到了組合,查看代碼code

class Game_role():
    def __init__(self,name,ad,hp):
        self.name = name
        self.ad = ad
        self.hp = hp
    def attack(self,obj):
        obj.hp -= self.ad
        print('{}打了{}一次,{}丟失{}點血量,如今血量爲{}'.format(self.name,obj.name,obj.name,self.ad,obj.hp))
    def equip_weapon(self,obj):
        self.weapon = obj


class Weapon():
    def __init__(self,name,ad):
        self.name = name
        self.ad = ad

    def attack_weapon(self,obj1,obj2):
        obj2.hp -=(self.ad+obj1.ad)
        print('{}用{}打了{}一下,{}還剩{}點血量'.format(obj1.name,self.name,obj2.name,obj2.name,obj2.hp))

r1 = Game_role('蓋倫',15,200)
r2 = Game_role('劍姬',30,140)
w1 = Weapon('三相',20)
r1.equip_weapon(w1)
r1.weapon.attack_weapon(r1,r2)
#結果
蓋倫用三相打了劍姬一下,劍姬還剩105點血量

這個算最基本的類之間的組合,對於類的組合,其實就是給一個對象封裝一個屬性,而這個屬性是另一個對象,也是最low的了。不過類的組合也是有好處的

  • 代碼之間的關係更加合理
  • 類與類之間的耦合性加強

一些小的測試題

1,暴力摩托程序(完成下列需求):
1.1建立三個遊戲人物,分別是:
•    蒼井井,女,18,攻擊力ad爲20,血量200
•    東尼木木,男,20,攻擊力ad爲30,血量150
•    波多多,女,19,攻擊力ad爲50,血量80
1.2建立三個遊戲武器,分別是:
•    平底鍋,ad爲20
•    斧子,ad爲50
•    雙節棍,ad爲65

1.3 建立三個遊戲摩托車,分別是:

•    小踏板,速度60邁
•    雅馬哈,速度80邁
•    寶馬,速度120邁。

完成下列需求(利用武器打人掉的血量爲武器的ad + 人的ad):
(1)蒼井井騎着小踏板開着60邁的車行駛在賽道上。
(2)東尼木木騎着寶馬開着120邁的車行駛在賽道上。
(3)波多多騎着雅馬哈開着80邁的車行駛在賽道上。
(4)蒼井井赤手空拳打了波多多20滴血,波多多還剩xx血。
(5)東尼木木赤手空拳打了波多多30滴血,波多多還剩xx血。
(6)波多多利用平底鍋打了蒼井井一平底鍋,蒼井井還剩xx血。
(7)波多多利用斧子打了東尼木木一斧子,東尼木木還剩xx血。
(8)蒼井井騎着寶馬打了騎着小踏板的東尼木木一雙節棍,東尼木木哭了,還剩xx血。(選作)
(9)波多多騎着小踏板打了騎着雅馬哈的東尼木木一斧子,東尼木木哭了,還剩xx血。

  1.2類的依賴

類的依賴:給一個類的方法傳了一個參數,而這個參數是一個類的對象,這種依賴關係是關係中緊密型最低的,耦合性最低的,就像上面的lol中人自身的攻擊手段,他調用的是對方的對象,這個時候蓋倫中有劍姬,而劍姬沒有蓋倫,並非像武器,武器已經徹底變成蓋倫的屬性而存在,緊密結合。用大象關冰箱來作一個測試。

class Elephant():
    def __init__(self,name):
        self.name = name

    def open_door(self,obj):
        print('1,2,3,開門')
        obj.open_()

    def close_door(self,obj):
        print('3,2,1,關門 ')
        obj.close_()

class Refrigerator():
    def __init__(self,name):
        self.name = name

    def open_(self):
        print('門正在打開')

    def close_(self):
        print('門正在關閉')
e1 = Elephant('神奇的大象')
r1 = Refrigerator('海爾')
e1.open_door(r1)
e1.close_door(r1)

和上面的相比,此次對象一樣調用了另外的obj,但是隻是使用了方法,並無讓obj成爲本身的屬性

  1.3類的關聯,聚合關係,組合關係

其實這個關係仍是組合,仍是舉倆個例子吧,一個陪女朋友吃飯,一個是教師關聯學校

陪女朋友吃飯

class Boy():
    def __init__(self,name,girelfriend=None):
        self.name = name
        self.girlfrind = girelfriend

    def have_dinner(self):
        if self.girlfrind == None:
            print('單身狗,吃啥吃!')
        else:
            print('%s 與%s共進晚餐'%(self.name,self.girlfrind.name))

    def get_girlfrind(self,obj):
        self.girlfrind = obj

    def lose_girlfriend(self):
        self.girlfrind = None


class Girl():
    def __init__(self,name):
        self.name = name
b1 = Boy('屌絲')#無女朋友
g1 = Girl('美女')
b2 = Boy('小哥',g1)#有娃娃親

b1.have_dinner()
b2.have_dinner()

教師關聯學校

class School():
    def __init__(self,city,address):
        self.city = city
        self.address = address
        self.teacher_list = []

class Teacher():
    def __init__(self,name,language):
        self.name = name
        self.language = language
        self.school = None

    def binding(self,li):
        for i,j in enumerate(li):
            print(i,'學校所在地址爲%s'%j.city)
        choice = int(input('你所選擇的學校的序號>>>').strip())
        self.school = li[choice]#教師綁定了學校
        li[choice].teacher_list.append(self)#學校綁定了教師
        print('學校綁定教師成功')


school1 =School('北京','昌平')
school2 =School('上海','張江')

li = [school1,school2]

test1 =Teacher('alex','python')
test1.binding(li)

for i in school1.teacher_list:
    print(i.name,i.language)

2.類的繼承

就像人和狗都屬於動物,人和狗都有年齡啊什麼的共同方法,那麼咱們就能夠寫一個公共類,狗和人繼承好了

class Animal:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def eat(self):
        print('在吃東西')

class People(Animal):
    def __init__(self,name,age,sex,skin):
        #方式一
        Animal.__init__(self,name,age,sex)
        #方式二
        super().__init__(name,age,sex)
        self.skin = skin
    def eat(self):
        print('人在吃東西')
#對於狗也是同樣

  2.1類的單繼承

類的單繼承就是字面意思,一個類只有一個父類用於繼承,父類又稱之爲基類或者是超類,子類又稱之爲派生類

就像上面的例子,people類繼承了animal類,people類可使用父類的屬性以及方法。

查看子類的繼承狀況爲:print(類.__base__)

# 既要執行子類方法,又要執行父類方法
# 方法一: Aniaml.__init__(self,name,sex,age)
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)

# 方法二:super
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)
# def func(self):
#     pass
# self = 3
# func(self)

# p1 = Person('春哥','laddboy',18,'有思想')
# p1.eat()

對於單繼承就是這樣了。

  2.2類的多繼承

類的多繼承能夠這麼理解,就是一個兒子有多個爹,而後類的繼承就須要排序了。對於類的分類,主要有經典類與新式類倆種

  • 經典類:python 2.x系列中,不繼承基類object,對於類的查找屬於一條道走到黑,深度優先
  • 新式類:python 2.x系列中,繼承基類object,python3.x系列中,默認都是新式類,查找按照c3算法

經典類的查詢,如圖,就是一條道走到黑

TIM圖片20190118222933

TIM圖片20190118222943

新式類的查詢,採用c3算法

c3算法:# #mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )

TIM圖片20190118223047

下面是推倒過程

# #mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
mro(K(H,I)) =[K]+merge(mro(H),mro(I),[H,I])

mro(H(E,F)) = [H] +merge(mro(E),mro(F),[E,F])
mro(E(B,C)) = [E] +merge([B,A],[C,A],[B,C])
mro(E(B,C)) = [E,B] +merge([A],[C,A],[C])

mro(E(B,C)) = [E,B,C,A]

mro(F(C,D)) = [F] +merge([C,A],[D,A],[C,D])
mro(F(C,D)) = [F,C] +merge([A],[D,A],[D])

mro(F(C,D)) = [F,C,D,A]

mro(H(E,F)) = [H] +merge([E,B,C,A],[F,C,D,A],[E,F])
mro(H(E,F)) = [H,E] +merge([B,C,A],[F,C,D,A],[F])
mro(H(E,F)) = [H,E,B] +merge([C,A],[F,C,D,A],[F])
mro(H(E,F)) = [H,E,B,F] +merge([C,A],[C,D,A])
mro(H(E,F)) = [H,E,B,F,C,] +merge([A],[D,A])

mro(H(E,F)) = [H,E,B,F,C,D,A]

mro(I(F,G)) =[I] +merge(mro(F),mro(G),[F,G])
mro(F(C,D)) = [F] +merge(mro[C],mro[D],[C,D])
mro(F(C,D)) = [F] +merge([C,A],D,A,[C,D])
mro(F(C,D)) = [F,C] +merge([A],D,A,[D])

mro(F(C,D)) = [F,C,D,A]

mro(G)=[D,A]

mro(I(F,G)) =[I] +merge([F,C,D,A],[G,D,A],[F,G])
mro(I(F,G)) =[I,F] +merge([C,D,A],[G,D,A],[G])
mro(I(F,G)) =[I,F,C] +merge([D,A],[G,D,A],[G])
mro(I(F,G)) =[I,F,C,G] +merge([D,A],[D,A])
mro(I(F,G)) =[I,F,C,G,D,A]

mro(K(H,I)) =[K]+merge([H,E,B,F,C,D,A],[I,F,C,G,D,A],[H,I])
mro(K(H,I)) =[K,H]+merge([E,B,F,C,D,A],[I,F,C,G,D,A],[I])
mro(K(H,I)) =[K,H,E,B,I]+merge([F,C,D,A],[F,C,G,D,A])
mro(K(H,I)) =[K,H,E,B,I,F,C]+merge([D,A],[G,D,A])
mro(K(H,I)) =[K,H,E,B,I,F,C,G,D,A]
#最後的執行順序就是[K,H,E,B,I,F,C,G,D,A]

若是不用這種手算的推導式,其實能夠直接調用方法查看

print(K.__mro__)

#結果和上面是一致的

super()也是按照上面的c3算法來調用的

根據上面作一些派生

子類能夠添加本身新的屬性或者在本身這裏定義這些屬性(不會影響父類),須要注意的是,一旦從新定義了與父類相同的名字的屬性,會以本身爲準。

在子類中,新建的重名函數類型,在編輯函數功能時,有可能調用父類的相同名字的函數功能時,應該用普通函數方法同樣,所以即便是self也應該傳值進去,例如類名.func(參數)

繼承原理:

  • 子類會先於父類檢查
  • 多個父類會根據他們在列表中的順序被檢查
  • 若是對下一個類存在倆個合法選擇,選擇第一個父類

3.類的封裝

在講類的封裝的前提須要說一下類方法的隱藏

  3.1隱藏

類的結構中能夠分靜態屬性以及動態屬性,按照另一個角度,能夠劃分爲公有,私有屬性

class Boss():
    name = 'alex'
    __secretary =['女一','男二','野模']#私有靜態屬性

    def __init__(self,username,password):
        self.username = username
        self.__password = password#私有對象屬性

    def func(self):
        print('老闆在辦公')

    def __func1(self):
        print('老闆在和祕書辦公')#私有方法

    def print(self):
        print(self.__secretary)
        self.__func1()
b1 = Boss('alex',123)

print(b1.name)
#print(b1.__secretary)
b1.print()

咱們會發現,按照之前的同樣的調用方法,根本得不到這些私有屬性以及私有方法,若是咱們在類內定義一個函數專門用於調用這些私有方法,才能看到這些私有屬性,單獨的去調用這些私有屬性只會報錯沒有這些方法,咱們會發現只有類的內部才能調用,類的外部以及派生類根本沒法調用屬性。

不過並非徹底沒法調用,當咱們看類的__dict__方法時,咱們會發現,python對類內部這些私有方法屬性進行了包裝

{'_Boss__secretary': ['女一', '男二', '野模'], '__doc__': None, '__init__': <function Boss.__init__ at 0x016246A8>, 'name': 'alex', '_Boss__func1': <function Boss.__func1 at 0x016D40C0>, 'print': <function Boss.print at 0x016D4078>, '__weakref__': <attribute '__weakref__' of 'Boss' objects>, '__dict__': <attribute '__dict__' of 'Boss' objects>, 'func': <function Boss.func at 0x016D4030>, '__module__': '__main__'}

咱們採用_boss__password這樣的形式仍是能夠調用的。不過通常不建議這麼使用,變形後的注意問題

  • 這種隱藏並非真正意思上的隱藏
  • 變形的過程發生在類的定義發生一次,定義後的操做,不會變形
  • 在繼承中,若是父類不想子類覆蓋子類本身的方法,能夠將方法定義爲私有,子類就不會繼承到這些方法

  3.2封裝的意義

封裝的意義爲

  • 封裝數據:明確區分類內外的,控制外部對隱藏屬性的操做
  • 封裝方法:隔離複雜度,只須要提供接口給其餘人使用便可

  3.3propetry函數

propetry也叫作類的私有屬性

class Market():
    def __init__(self,name,prix,discount):
        self.name = name
        self.__prix = prix
        self.__discount = discount

    @property#設定爲私有方法
    def price(self):
        return self.__prix*self.__discount

    @price.setter#對私有方法的內容進行修改
    def price(self,new_price):
        self.__prix = new_price

    @price.deleter#對私有方法的內容進行刪除,不過不是常常用
    def price(self):
        del self.__prix

m1 = Market('洗髮水',45,0.8)
print(m1.price)
m1.price = 48
print(m1.price)
del m1.price
print(m1.price)

將一個類的方法修改成私有方法,這樣就假裝成屬性,雖然在代碼邏輯上沒有什麼提升,可是會讓執行起來更合理一些,

4.類的多態

多態指的是一類事物有多種形態,好比:動物有多種形態:人,狗,豬

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

  4.1類的多態性

多態性是指在不考慮實例類型的狀況下使用實例,多態性分爲靜態多態性和動態多態性

靜態多態性:如任何類型均可以用運算符+進行運算

動態多態性:以下

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

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

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

其實你們從上面多態性的例子能夠看出,咱們並無增長什麼新的知識,也就是說python自己就是支持多態性的,這麼作的好處是什麼呢?

  • 增長了程序的靈活性:以不變應萬變,不論對象變幻無窮,使用者都是同一種形式去調用,如func(animal)
  • 增長了程序額可擴展性:經過繼承animal類建立了一個新的類,使用者無需更改本身的代碼,仍是用func(animal)去調用  

  4.2鴨子方法

Python崇尚鴨子類型,即‘若是看起來像、叫聲像並且走起路來像鴨子,那麼它就是鴨子’

python程序員一般根據這種行爲來編寫程序。例如,若是想編寫現有對象的自定義版本,能夠繼承該對象

也能夠建立一個外觀和行爲像,但與它無任何關係的全新對象,後者一般用於保存程序組件的鬆耦合度。

例1:利用標準庫中定義的各類‘與文件相似’的對象,儘管這些對象的工做方式像文件,但他們沒有繼承內置文件對象的方法

#兩者都像鴨子,兩者看起來都像文件,於是就能夠當文件同樣去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass
相關文章
相關標籤/搜索