python 全棧開發,Day19(組合,組合實例,初識面向對象小結,初識繼承)

1、組合

表示的一種什麼有什麼的關係python

 

先來講一下,__init__的做用面試

class Dog:
    def __init__(self, name, kind, hp, ad):
        self.name = name  # 對象屬性 屬性
        self.kind = kind
        self.hp = hp
        self.ad = ad

    def bite(self, p):
        p.hp -= self.ad  # 人掉血
        print('%s咬了%s一口,%s掉了%s點血' % (self.name, p.name, p.name, self.ad))

實例化A和Bapp

A = Dog('笨笨', 'teddy', 50, 10)
B = Dog('花花')

A職員是老員工,他知道這個遊戲,狗有什麼屬性。ui

B是新來的,假如沒有__init__方法,B就隨便傳參數了,可是類方法執行時,會報錯。spa

爲了不這個問題,在__init__方法裏面,約束某些屬性,必需要傳,不然方法執行出錯。3d

 

人狗大戰遊戲,如今須要增長武器指針

武器是人的一個屬性,好比攻擊力,磨損度,價格,名字,品級,技能對象

增長一個類blog

class Weapon:
    def __init__(self, name, price, level, ad):
        self.name = name
        self.price = price
        self.level = level
        self.ad = ad * self.level  # 升級以後,攻擊就翻倍了
        self.wear = 20  # 默認的耐久度,實例化時,能夠不用傳

    def skill(self, dog):  # 技能
        dog.hp -= self.ad
        print('%s受到了%s點的傷害,%s掉了%s點血' % (dog.name, self.name, dog.name, self.ad))

實例化一個武器繼承

axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭

 

2、組合實例

1.人狗大戰

武器給誰裝備呢?武器須要花錢買吧,那麼就須要玩家充錢,如今加一個充錢功能

完整代碼以下:

class Person:
    def __init__(self, name, sex, hp, ad):
        self.name = name  # 對象屬性 屬性
        self.sex = sex
        self.hp = hp  # 血量
        self.ad = ad  # 攻擊力
        self.money = 0  # 金額
        self.arms = None  # 默認武器爲None
def attack(self, d): d.hp -= self.ad print('%s攻擊了%s,%s掉了%s點血' % (self.name, d.name, d.name, self.ad)) def pay(self): # 充值 money = int(input('請輸入您要充值的金額:')) self.money += money print('您的餘額是:%s' % self.money) def wear(self, weapon): # 裝備武器 if self.money >= weapon.price: self.arms = weapon # 組合 給人裝備了武器 self.money -= weapon.price print('購買成功,您已經順利裝備了%s' % weapon.name) else: print('餘額不足,請充值!') def attack_with_weapon(self, dog): # 拿武器攻擊狗 if 'arms' in self.__dict__ and self.arms != None: # 若是武器屬性在實例屬性字典裏,而且屬性不爲空 self.arms.skill(dog) # 使用武器攻擊狗 else: print('請先裝備武器') class Dog: def __init__(self, name, kind, hp, ad): self.name = name # 對象屬性 屬性 self.kind = kind self.hp = hp self.ad = ad def bite(self, p): p.hp -= self.ad # 人掉血 print('%s咬了%s一口,%s掉了%s點血' % (self.name, p.name, p.name, self.ad)) class Weapon: # 武器 def __init__(self, name, price, level, ad): self.name = name # 武器名 self.price = price # 價格 self.level = level # 等級 self.ad = ad * self.level # 升級以後,攻擊就翻倍了 self.wear = 20 # 默認的耐久度,實例化時,能夠不用傳 def skill(self, dog): # 技能,攻擊狗 dog.hp -= self.ad # 狗掉血 print('%s受到了%s點的傷害,%s掉了%s點血' % (dog.name, self.name, dog.name, self.ad))

實例化武器,玩家購買武器,攻擊狗

alex = Person('a_sb', '不詳', 1, 5)
boss_jin = Person('金老闆', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10)
axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭

alex.pay()  # 充值
alex.wear(axe)  # 裝備武器斧頭
alex.arms.skill(teddy)  # 使用斧頭攻擊狗

執行輸出:

 注意:

不能加類靜態變量meny = 0
不然玩家充錢了,別的玩家就可使用了

 

int以後,不須要strip()一下,int會自動去除空格

這樣寫是一次性的,寫一個while循環,顯示菜單執行

實例化部分,改爲以下:

alex = Person('a_sb', '不詳', 1, 5)
boss_jin = Person('金老闆', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10)
axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭


lst = ['攻擊', '充值', '裝備武器', '使用武器攻擊']
while True:
    for index, value in enumerate(lst, 1):
        print(index, value)
    num = int(input('請選擇操做序號 >>>'))
    if num == 1:
        alex.attack(teddy)
    elif num == 2:
        alex.pay()
    elif num == 3:
        print('裝備前餘額 %s' % alex.money)
        alex.wear(axe)
        print('裝備後餘額 %s' % alex.money)
    elif num == 4:
        alex.attack_with_weapon(teddy)
    else:
        print('無效的序號')

執行輸出:

修改實例化部分,代碼以下:

alex.pay()  # 充值
alex.wear(axe)  # 裝備武器斧頭
print(alex.__dict__)  # 查看alex的屬性
print(axe)  # 查看aex

執行輸出:

請輸入您要充值的金額:2000
您的餘額是:2000
購買成功,您已經順利裝備了斧頭
{'sex': '不詳', 'hp': 1, 'name': 'a_sb', 'ad': 5, 'arms': <__main__.Weapon object at 0x00000250D273C6A0>, 'money': 1000}
<__main__.Weapon object at 0x00000250D273C6A0>

 

能夠發現alex的arms屬性和axe的內存地址,是一摸同樣的。

skill方法,只有武器纔有,人是不能直接調用的。可是,人一旦裝備上了武器,就能夠執行skill方法。

關鍵點,就在於如下一段代碼

self.arms = weapon  # 組合 給人裝備了武器

直接給人加了一個屬性arms,注意,等式右邊的weapon是一個武器對象

因此就可使用武器攻擊狗

self.arms.skill(dog)

self也就是實例對象,好比alex

 

當一個類的對象做爲另外一個類的屬性,說明這2個類組合在一塊兒了。

那麼類就可使用另一個類的屬性和方法了。

通常說組合 ,是指2個類的組合

修改實例化部分

alex = Person('a_sb', '不詳', 1, 5)
boss_jin = Person('金老闆', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10)
axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭

alex.pay()  # 充值
alex.wear(axe)  # 裝備武器斧頭
print(alex.arms.__dict__)  # 查看axe實例,也就是Weapon類的全部屬性
alex.arms.skill(teddy)  # 執行

執行輸出:

請輸入您要充值的金額:2000
您的餘額是:2000
購買成功,您已經順利裝備了斧頭
{'price': 1000, 'ad': 100, 'level': 100, 'wear': 20, 'name': '斧頭'}
笨笨受到了斧頭點的傷害,笨笨掉了100點血

 

爲何會用組合 :獨立的對象不能發揮他的做用,必須依賴一個對象

好比上面的例子,斧頭不可以攻擊狗,它依賴人來執行。

修改實例化部分

alex = Person('a_sb', '不詳', 1, 5)
boss_jin = Person('金老闆', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10)
axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭

axe.skill(teddy)  # 直接用斧頭攻擊狗

執行輸出:

笨笨受到了斧頭點的傷害,笨笨掉了100點血

 

這樣就不對了

人還能夠裝備2件武器

alex = Person('a_sb', '不詳', 1, 5)
boss_jin = Person('金老闆', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10)
axe = Weapon('斧頭', 1000, 100, 1)  # 斧頭
knife = Weapon('刀', 1000, 100, 1)  # 刀

alex.pay()  # 充值
alex.wear(axe)  # 裝備武器斧頭
alex.wear(knife)  # 裝備武器刀
alex.arms.skill(teddy)  # 執行攻擊

執行輸出:

請輸入您要充值的金額:2000
您的餘額是:2000
購買成功,您已經順利裝備了斧頭
購買成功,您已經順利裝備了刀
笨笨受到了刀點的傷害,笨笨掉了100點血

 

2.圓環

圓形類
寫一個圓環類 組合 圓形類 去完成 計算圓環的面積和周長
一個類的對象做爲另外一個類對象的屬性
圓環中有圓

圓形類 : 計算圓形面積 和 周長
圓環類 :
圓環的周長 : 大圓周長加小圓周長
圓環的面積 : 大圓的面積 - 小圓的面積

 

看昨天寫的圓環

from math import pi


class Ring1:
    def __init__(self, out_r, in_r):
        self.out_r = out_r
        self.in_r = in_r

    def cal_area(self):
        return abs(pi * self.out_r ** 2 - pi * self.in_r ** 2)

    def cal_perimeter(self):
        return pi * self.out_r * 2 + pi * self.in_r * 2


r1 = Ring1(10, 5)
print(r1.cal_area())
print(r1.cal_perimeter())

執行輸出:

235.61944901923448
94.24777960769379

 

改爲組合方式

from math import pi


class Circle:  # 圓形的面積公式不會變
    def __init__(self, r):
        self.r = r

    def cal_area(self):  # 面積
        return pi * self.r ** 2

    def cal_perimeter(self):  # 周長
        return pi * self.r * 2


class Ring2:  # 圓環
    def __init__(self, out_r, in_r):
        self.out_circle = Circle(out_r)  # 實例化圓做爲圓環的大圓
        self.in_circle = Circle(in_r)  # 實例化圓,做爲圓環的小圓

    def area(self):  # 圓環面積
#用絕對值,保證結果不爲負數 return abs(self.out_circle.cal_area() - self.in_circle.cal_area()) # 大圓面積 - 小圓面積 def cal_perimeter(self): return self.out_circle.cal_perimeter() + self.in_circle.cal_perimeter() # 大圓周長 + 小圓周長 r1 = Ring2(10, 5) # 實例化圓環,傳入大圓和小圓的半徑 print(r1.area()) print(r1.cal_perimeter())

執行輸出:

235.61944901923448
94.24777960769379

 

在這個例子中,圓環包含了圓,而圓的面積和周長公式,和圓環有點相似。

因此,能夠把圓,這個對象,做爲圓環類對象的屬性,這就是組合。

 

3.老師和班級

老師
屬性:姓名 年齡 性別 班級 : s11

班級
屬性:班級名 班級人數 科目 性質

class Clas:
    def __init__(self, name, num, course, type):
        self.name = name
        self.num = num
        self.course = course
        self.type = type


class Teacher:
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age


py11 = Clas('超級無敵s11', 89, 'python', '脫產全棧')  # 實例化一個班級
print(py11.course)  # 查看課程

boss_jin = Teacher('太白', '?', 40)  # 實例化一個老師

執行輸出:

python

 

那麼老師怎麼和班級關聯呢?

直接給Teacher類加一個屬性cls,表示班級

class Teacher:
    def __init__(self, name, sex, age, cls):
        self.name = name
        self.sex = sex
        self.age = age
        self.cls = cls  # 班級

實例化時,最後一個參數,直接傳對象py11

py11 = Clas('超級無敵s11', 89, 'python', '脫產全棧')  # 實例化一個班級
print(py11.course)  # 查看課程

boss_jin = Teacher('太白', '?', 40, py11)  # 實例化一個老師
print(boss_jin.cls.course)  # 查看課程
print(boss_jin.cls.__dict__)  # 查看py11的全部屬性

執行輸出:

python
python
{'num': 89, 'course': 'python', 'type': '脫產全棧', 'name': '超級無敵s11'}

 

若是人數變了,Teacher也能感知到

py11 = Clas('超級無敵s11', 88, 'python', '脫產全棧')  # 實例化一個班級
print(py11.course)  # 查看課程

boss_jin = Teacher('太白', '?', 40, py11)  # 實例化一個老師
print(boss_jin.cls.num)  # 查看人數

執行輸出:

python
88

 

經過實例化,能夠直接組合2個類。

在這個例子中,Teacher經過cls屬性,獲得了py11對象的全部屬性以及方法。

這是組合的第二種使用方式

 

3、初識面向對象小結

面向對象的思想

    不關注程序執行的過程

    關心的是一個程序中的角色,以及角色與角色之間的關係

在python中,一切皆對象

類:list

對象,實例: [1,2,3,4,5]

看list的源代碼

class list(object):
    """
    list() -> new empty list
    list(iterable) -> new list initialized from iterable's items
    """

發現,list也是一個對象,它有不少方法

類是給別人使用的,好比list.append()
類轉換爲對象,是實例化的過程

實例化的過程

    建立一個對象

    __init__給對象添加一些屬性,對象默認的名字self

    將self所指向的內存空間返回給實例化它的地方

使用這個對象能夠找到兩個東西

    對象所在的內存空間中存儲的屬性

    類對象指針,指向類中全部方法和靜態屬性

對象找名字的時候:先找本身內存空間中的,再找類的

對象沒有權利修改類中的靜態變量和方法,好比self.類靜態變量

若是修改了,那麼就存在本身的對象空間裏面

用類名操做靜態變量(屬性),可讓全局生效

類名:實例化對象 調用靜態屬性 執行方法

交互:對象能夠做爲參數傳遞給類中的方法

組合:對象能夠做爲一個對象的屬性--什麼有什麼的關係

到處都是組合和交互

class B:pass
class A:
    def func(self,aaa):
        print(aaa)

a = A()
b = B()
a.func(b)

執行輸出:

<__main__.B object at 0x000002CD1677C780>

 

class B:pass
class A:
    def func(self,aaa):
        print(aaa)

a = A()
b = B()
a.func('666')

執行輸出:666

這也是組合,666是str對象,它也是交互,它是python內置的對象和自定義的對象組合

 

class B:pass
class A:
    def func(self,aaa):
        self.aaa = aaa

a = A()
b = B()
a.func('666')  # 傳值給aaa
print(a.aaa)

執行輸出:

666

 

class B:pass
class A:
    def func(self,aaa):
        self.aaa = aaa

a = A()
b = B()
a.func('666')
print(a.aaa.startswith('6'))

執行輸出:True

 

4、初識繼承

面向對象的三大特性:

繼承,多態,封裝

這3大特性是全部面嚮對象語言特色

 

好比遊戲的例子

人物:名字,角色,性別,職業,技能

    治療

        回血

        醫生

        護士

    輸出

    防護

看以下代碼:

class Person:
    def __init__(self, name, hp, ad):
        self.name = name
        self.hp = hp
        self.ad = ad
      
        
class Dog:
    def __init__(self, name, hp, ad):
        self.name = name
        self.hp = hp
        self.ad = ad

Person和dog有相同的屬性,那麼應該有某種聯繫

 

類與類直接的關係:什麼是什麼的關係

class Parent:pass
class Son(Parent):pass #繼承關係,Son繼承了Parent
print(Son.__bases__) #內置的屬性,查看父類
print(Parent.__bases__)

執行輸出:

(<class '__main__.Parent'>,)
(<class 'object'>,)

 

能夠看出Son的父類是Parent,Parent的父類是object

在python3中,全部的類都會默認繼承object類
繼承了object類的全部類都是新式類
若是一個類沒有繼承任何父類,那麼__bases__屬性就會顯示<class 'object'>

 

繼承通常有2種:單繼承和多繼承

單繼承

class Parent:pass
class Son(Person):pass 

多繼承

class Parent1:pass
class Parent2(Parent1):pass
class Parent3:pass
class Son(Parent2,Parent3):pass   # 繼承關係
print(Parent2.__bases__)
print(Son.__bases__)

執行輸出:

(<class '__main__.Parent1'>,)
(<class '__main__.Parent2'>, <class '__main__.Parent3'>)

 

父類 :別名有2個,基類,超類
子類 :別名 派生類

 

先有了人 狗兩個類
發現兩個類有相同的屬性、方法
人和狗 都是遊戲裏的角色
抽象出一個animal類型
繼承

class Animal:
    role = 'Animal'
    def __init__(self,name,hp,ad):
        self.name = name     # 對象屬性 屬性
        self.hp = hp         #血量
        self.ad = ad         #攻擊力

class Person(Animal):pass
class Dog(Animal):pass
alex = Person()

執行報錯:

TypeError: __init__() missing 3 required positional arguments: 'name', 'hp', and 'ad'

缺乏3個參數,繼承了父類的init方法

 

再次實例化

alex = Person('alex',10,5)
print(alex)  # 查看父類
print(alex.__dict__)  # 查看屬性

執行輸出:

<__main__.Person object at 0x000001DA0687C7B8>
{'hp': 10, 'name': 'alex', 'ad': 5}

 

添加狗實例

alex = Person('alex',10,5)
print(alex)
print(alex.__dict__)
dog = Dog('teddy',100,20)
print(dog)
print(dog.__dict__)

執行輸出:

<__main__.Person object at 0x0000015E6983C710>
{'ad': 5, 'name': 'alex', 'hp': 10}
<__main__.Dog object at 0x0000015E6983C7B8>
{'ad': 20, 'name': 'teddy', 'hp': 100}

自個,有各自的屬性

class Animal:
    role = 'Animal'
    def __init__(self,name,hp,ad):
        self.name = name     # 對象屬性 屬性
        self.hp = hp         #血量
        self.ad = ad         #攻擊力

    def eat(self):
        print('%s吃藥回血了'%self.name)

class Person(Animal):
    r = 'Person'
    def attack(self,dog):   # 派生方法
        print("%s攻擊了%s"%(self.name,dog.name))

    def eat2(self):
        print('執行了Person類的eat方法')
        self.money = 100
        self.money -= 10
        self.hp += 10

class Dog(Animal):
    def bite(self,person):  # 派生方法
        print("%s咬了%s" % (self.name, person.name))
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.attack(dog) #繼承中的派生方法
alex.eat()  #繼承父類方法本身沒有同名方法
alex.eat2()
dog.eat()

執行輸出:

alex攻擊了teddy
alex吃藥回血了
執行了Person類的eat方法
teddy吃藥回血了

 

 init始終在父類裏面,執行了alex實例化,返回給alex

 

alex實例化時,建立一個實例命令空間

alex.attack(dog) 直接調用,由於實例空間中存在

alex.eat() 實例空間找不到,取找類對象指針,指針指向父類Person,今後類中找到了eat方法。

....

 

對象使用名字的順序

alex = Person('alex',10,5)
alex.eat = 'aaa' #增長一個不存在的方法,名字和父類方法名同樣
print(alex.eat)

  指向輸出:

aaa

 

alex = Person('alex',10,5)
alex.eat = 'aaa'
print(alex.eat())

指向報錯:

TypeError: 'str' object is not callable

由於此時eat是字符串,不是方法

 

對象使用名字順序:

先找對象本身內存空間中的,再找對象本身類中的,再找父類中的

 

經典面試題

class Parent:
    def func(self):
        print('in parent func')
    def __init__(self):
        self.func()

class Son(Parent):
    def func(self):
        print('in son func')

s = Son()

上面的代碼執行的是son仍是parent中的方法?

執行輸出:

in son func

 

 

分析:

s = Son() 建立實例命名空間s

Son繼承了Parent,類對象指針指向Son和Parent
Son沒有__init__方法,它繼承了Parent的__init__方法。
執行__init__方法,此時self是指向Son的
執行self.func()
根據查找順序,本身->本身的類->父類
因爲本身沒有,從類中找,發現了,執行func
最終輸出in son func

 總結:

self.名字的時候,不要看self當前在哪一個類裏,要看這個self究竟是誰的對象

 

來一張大神的圖片

 

 

做業 :

讀博客
把實例全走一遍
默寫 圓環類與圓類組合

 

 圓環類與圓類組合

from math import pi
 
 
class Circle:  # 圓形的面積公式不會變
    def __init__(self, r):
        self.r = r
 
    def cal_area(self):  # 面積
        return pi * self.r ** 2
 
    def cal_perimeter(self):  # 周長
        return pi * self.r * 2
 
 
class Ring2:  # 圓環
    def __init__(self, out_r, in_r):
        self.out_circle = Circle(out_r)  # 實例化圓做爲圓環的大圓
        self.in_circle = Circle(in_r)  # 實例化圓,做爲圓環的小圓
 
    def area(self):  # 圓環面積<br>        #用絕對值,保證結果不爲負數
        return abs(self.out_circle.cal_area() - self.in_circle.cal_area())  # 大圓面積 - 小圓面積
 
    def cal_perimeter(self):
        return self.out_circle.cal_perimeter() + self.in_circle.cal_perimeter()  # 大圓周長 + 小圓周長
 
 
r1 = Ring2(10, 5)  # 實例化圓環,傳入大圓和小圓的半徑
print(r1.area())
print(r1.cal_perimeter())
相關文章
相關標籤/搜索