表示的一種什麼有什麼的關係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) # 斧頭
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對象的全部屬性以及方法。
這是組合的第二種使用方式
面向對象的思想
不關注程序執行的過程
關心的是一個程序中的角色,以及角色與角色之間的關係
在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
面向對象的三大特性:
繼承,多態,封裝
這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())