2020年9月16日星期三

[課後作業] 第038講:類和對象:繼承|課後測試題及答案

[課後作業] 第038講:類和對象:繼承|課後測試題及答案




《零基礎入門學習Python》視頻下載地址:傳送門ltRob7
IDhQ".JSxgCwt8YN`&}e5$|EG4Ai
測試題:
bO"EUd#;|:^kg5F3qm'}Bn`9PI~ 1=
0.繼承機制給程序猿帶來最明顯的好處是?#Db=t
$'l7N-( `@=OIgt8S6DjRy}{
p8od`9](n^D#"W%@RcwN5r>U-!){;
1.如果按以下方式重寫魔法方法__init__,結果會怎樣?
|#1n9~*,

  1. class MyClass:
  2.     def __init__(self):
  3.         return "I love FishC.com!"
複製代碼

bruQ9]6{deEYa)V_(cMo=tB.$0* ~
2.當子類定義了與相同名字的屬性或方法時,Python是否會自動刪除父類的相關屬性或方法?Le,qy
5t2s"l =MrGK~e0XS'v.)`7!
9qm&Q%w'U+b.~4dk*Xj]MT> H^3OhL
3.假設已經有鳥類的定義,現在我要定義企鵝類繼承於鳥類,但我們都知道企鵝是不會飛的,我們應該如何屏蔽父類(鳥類)中飛的方法?=+d;jC}0
6$+C(HUmKvuG)0*"9-Lfszj'
vF]AaD0& ^lK_.c)j2X8'p[
4. super函數有什麼“超級”的地方?i~R_hqvPM
rnI=4~b*Shm(%w"`AY-,'jD1ek!NJ;
=F~}L5xD1OmJ:Ns><kp%
5.多重繼承使用不當會導致重複調用(也叫鑽石繼承、菱形繼承)的問題,請分析以下代碼在實際編程中有可能導致什麼問題?KX2l+-gs>t

  1. class A():
  2.     def __init__(self):
  3.         print("進入A…")
  4.         print("離開A…")

  5. class B(A):
  6.     def __init__(self):
  7.         print("進入B…")
  8.         A.__init__(self)
  9.         print("離開B…")
  10.         
  11. class C(A):
  12.     def __init__(self):
  13.         print("進入C…")
  14.         A.__init__(self)
  15.         print("離開C…")

  16. class D(B, C):
  17.     def __init__(self):
  18.         print("進入D…")
  19.         B.__init__(self)
  20.         C.__init__(self)
  21.         print("離開D…")
複製代碼

$a-~*:+>?({_zemK;uL7t=Y
6.如何解決上一題中出現的問題?版權屬於:bbs.fishc.com
_H$p-dzcPY!UhGXA8tWK)s
N>puH~B ,i$M"zVwWKQ|f#+?0
動動手:
|M; i@HL~st$X%uTrZn.I[5GJSzN`-
0.定義一個點(Point)類和直線(Line)類,使用getLen方法可以獲得直線的長度。`?^$q=:dI
MZ>F$ACaqHB7]e^cwvSh|Dm)4[%2G
提示:
  • 設點A(X1,Y1)、點B(X2,Y2),則兩點構成的直線長度|AB| = √((x1-x2)2+(y1-y2)2)
  • Python 中計算開根號可使用math 模塊中的sqrt 函數
  • 直線需有兩點構成,因此初始化時需有兩個點(Point)對像作為參數

=uBhf|0?3b8C!HAYgXl@pT<)q
1.展示一個你的作品:你已經掌握了Python大部分的基礎知識,要開始學會自食其力了!請花一個星期做一個你能做出來的最好的作品(可以是遊戲、應用軟件、腳本),使用上你學過的任何東西(類,函數,字典,列表……)來改進你的程序。



2.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!THpz>$-SQ;^Knr|kG)tMhma8{:
oQ=dx"t$9)0Swn'aO}1T6


回复您的答案即可查看參考答案!"Q>Z@PMt
1t0m.8M5xfRP_l"jH{9@F=
1Y6g[95Dd0'kRFT]yiz#HL
測試題答案:Powered by bbs.fishc.com

本帖隱藏的內容

來自:bbs.fishc.com
0.繼承機制給程序猿帶來最明顯的好處是?RL:|WgfwD
qD}g]G1R6.,fm`(QkC@MWK*5
答:可以偷懶,據說這是每一個優秀程序猿的夢想!RXb7V
O@4U(7,>D)6G%yJo*| 1ia}Z&<']
如果一個類A繼承自另一個類B,就把這個A稱為B的子類,把B稱為A的父類、基類或超類。繼承可以使得子類具有父類的各種屬性和方法,而不需要再次編寫相同的代碼(偷懶)。Ag!j:`mL
i}BDdS?"o.aW_ZqXb4+(LFk[
在子類繼承父類的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類的原有屬性和方法,使其獲得與父類不同的功能。另外,為子類追加新的屬性和方法也是常見的做法。P4DeG?,
~Qka1H9&?"Ewz:2*V4WC xT
dG<@vi5`PX(LUb*e{6!|}3~Y'ExT
1.如果按以下方式重寫魔法方法__init__,結果會怎樣?jIpA9H}v
  1. class MyClass:
  2.         def __init__(self):
  3.                 return "I love FishC.com!"
複製代碼

答:會報錯,因為__init__特殊方法不應當返回除了None以外的任何對象。4qZnw
  1. >>> myClass = MyClass()
  2. Traceback (most recent call last):
  3.   File "<pyshell#13>", line 1, in <module>
  4.     myClass = MyClass()
  5. TypeError: __init__() should return None, not 'str'
複製代碼

srR25NdW]{L%F)#$`pq>8.Cu(Y|S[
2.當子類定義了與相同名字的屬性或方法時,Python是否會自動刪除父類的相關屬性或方法?fO& #6hk*K=
h,}]HrGx0W?9Xl+:%zN=yQIZc`ef<
答:不會刪除!Python的做法跟其他大部分面向對象編程語言一樣,都是將父類屬性或方法覆蓋,子類對象調用的時候會調用到覆蓋後的新屬性或方法,但父類的仍然還在,只是子類對象“看不到”。Q])bM7tvxr
<,}7~P>+V(:ge1 |6?JyM=!o$IR
6p$]e{`.GE7T"bd2<D%'^_
3.假設已經有鳥類的定義,現在我要定義企鵝類繼承於鳥類,但我們都知道企鵝是不會飛的,我們應該如何屏蔽父類(鳥類)中飛的方法?rm3Nyn^HU
@"n<Hd%?;(WfbDXup]w|~.6lm
答:覆蓋父類方法,例如將函數體內容寫pass,這樣調用fly方法就沒有任何反應了。o#`Fm
  1. class Bird:
  2.         def fly(self):
  3.                 print("Fly away!")

  4. class Penguin(Bird):
  5.         def fly(self):
  6.                 pass

  7. >>> bird = Bird()
  8. >>> penguin = Penguin()
  9. >>> bird.fly()
  10. Fly away!
  11. >>> penguin.fly()
複製代碼

tPgs(m-!+EC`L'Jn*FZf
4. super函數有什麼“超級”的地方?R5t&3]_J
J2LRj1O"FKuBM_cA[VE.I:N7(,t
答:super函數超級之處在於你不需要明確給出任何基類的名字,它會自動幫您找出所有基類以及對應的方法。由於你不用給出基類的名字,這就意味著你如果需要改變了類繼承關係,你只要改變class語句裡的父類即可,而不必在大量代碼中去修改所有被繼承的方法。`6a78*M}
iH~#SvKqVgQ0Bsx;=j)P!y:>R
L6#3>-uyvM^ 82gGE'!U7;iTx
5.多重繼承使用不當會導致重複調用(也叫鑽石繼承、菱形繼承)的問題,請分析以下代碼在實際編程中有可能導致什麼問題?>:DJS
  1. class A():
  2.     def __init__(self):
  3.         print("進入A…")
  4.         print("離開A…")

  5. class B(A):
  6.     def __init__(self):
  7.         print("進入B…")
  8.         A.__init__(self)
  9.         print("離開B…")
  10.         
  11. class C(A):
  12.     def __init__(self):
  13.         print("進入C…")
  14.         A.__init__(self)
  15.         print("離開C…")

  16. class D(B, C):
  17.     def __init__(self):
  18.         print("進入D…")
  19.         B.__init__(self)
  20.         C.__init__(self)
  21.         print("離開D…")
複製代碼

答:多重繼承容易導致重複調用問題,下邊實例化D類後我們發現A被前後進入了兩次(有童鞋說兩次就兩次憋,我女朋友還不止呢……)。[Yx4+q
I~sDaYnpr#"i'_Qz$!Z
這有什麼危害?我舉個例子,假設A的初始化方法裡有一個計數器,那這樣D一實例化,A的計數器就跑兩次(如果遭遇多個鑽石結構重疊還要更多),很明顯是不符合程序設計的初衷的(程序應該可控,而不能受到繼承關係影響)。nhxyJD[(Ag
"1sJ-0#`b%' ^]&fNCMVnmGd~[Q
  1. >>> d = D()
  2. 進入D…
  3. 進入B…
  4. 進入A…
  5. 離開A…
  6. 離開B…
  7. 進入C…
  8. 進入A…
  9. 離開A…
  10. 離開C…
  11. 離開D…
複製代碼

為了讓大家都明白,這裡只是舉例最簡單的鑽石繼承問題,在實際編程中,如果不注意多重繼承的使用,會導致比這個複雜N倍的現象,調試起來不是一般的痛苦……所以一定要盡量避免使用多重繼承。."M%$I;B>q
k%=X[qx.e+VDa4EQ)#9$I"rMc&5ouP
想更多的了解,閱讀-> 多重繼承的陷阱:鑽石繼承(菱形繼承)問題uf^ F&2aJ
}5Z>$EUN@X|tg+HAqf=Ia{kY uGyh
z5@q:)EYHPLX9v"ysK]V,{|hBdmJ
6.如何解決上一題中出現的問題?*+jX@R6Ws"
IBc4VXWpah5FN+ !<#9H_
答:super函數再次大顯神威。版權屬於:bbs.fishc.com
Z$iFtkL>0[X|%)K5{73-#G8(T
  1. class A():
  2.     def __init__(self):
  3.         print("進入A…")
  4.         print("離開A…")

  5. class B(A):
  6.     def __init__(self):
  7.         print("進入B…")
  8.         super().__init__()
  9.         print("離開B…")
  10.         
  11. class C(A):
  12.     def __init__(self):
  13.         print("進入C…")
  14.         super().__init__()
  15.         print("離開C…")

  16. class D(B, C):
  17.     def __init__(self):
  18.         print("進入D…")
  19.         super().__init__()
  20.         print("離開D…")

  21. >>> d = D()
  22. 進入D…
  23. 進入B…
  24. 進入C…
  25. 進入A…
  26. 離開A…
  27. 離開C…
  28. 離開B…
  29. 離開D…
複製代碼

動動手答案:來自:bbs.fishc.com

本帖隱藏的內容

版權屬於:bbs.fishc.com
0.定義一個點(Point)類和直線(Line)類,使用getLen方法可以獲得直線的長度。)_mr[v1a"c
L~P`oEN+H{$c:ivseWO?k^
提示:來自:bbs.fishc.com
  • 設點A(X1,Y1)、點B(X2,Y2),則兩點構成的直線長度|AB| = √((x1-x2)2+(y1-y2)2)
  • Python 中計算開根號可使用math 模塊中的sqrt 函數
  • 直線需有兩點構成,因此初始化時需有兩個點(Point)對像作為參數

代碼清單:Powered by bbs.fishc.com
y 45uU)`K2Cnj@s%#'8p$
  1. import math

  2. class Point():
  3.     def __init__(self, x=0, y=0):
  4.         self.x = x
  5.         self.y = y

  6.     def getX(self):
  7.         return self.x

  8.     def getY(self):
  9.         return self.y

  10. class Line():
  11.     def __init__(self, p1, p2):
  12.         self.x = p1.getX() - p2.getX()
  13.         self.y = p1.getY() - p2.getY()
  14.         self.len = math.sqrt(self.x*self.x + self.y*self.y)

  15.     def getLen(self):
  16.         return self.len

  17. >>> p1 = Point(1, 1)
  18. >>> p2 = Point(4, 5)
  19. >>> line = Line(p1, p2)
  20. >>> line.getLen()
  21. 5.0
複製代碼

1$fPov2t&3|Km^c`7;wxiG
1.展示一個你的作品:你已經掌握了Python大部分的基礎知識,要開始學會自食其力了!請花一個星期做一個你能做出來的最好的作品(可以是遊戲、應用軟件、腳本),使用上你學過的任何東西(類,函數,字典,列表……)來改進你的程序。$tPDWT7X(
'Djb$*owXmC&4Zq5Vd3)%G
答:這一次,我不打算提醒你要具體做點什麼和怎麼做,你需要自己來想創意。試著下手吧,編程就是解決問題的過程,這就意味著你要嘗試各種可能性,進行實驗,經歷失敗,然後丟掉你做出來的東西重頭開始!Z8{rlH!F7]
b"[_sfm F%?w(lvq$J]>&=D
當你被某個問題卡住的時候,你可以到論壇尋求幫助,把你的代碼貼出來給其他魚油看,爭取得到別人的建議並持續修改你的代碼,直到它讓你滿意為止!n'WKI[k@6
t1(=3pL|yzk,?P%nWx~N:F+T<m#
最後請將你的作品發佈在論壇的“ 作品展示區 ”,我會去幫你加分。YQauLk
1g&M}D_e$FU#8X"G:jVNAB=
=YKLokpIe4>Qm8&%~+R"


2.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

小甲魚希望你認真對待作業就像你希望小甲魚推出高質量視頻一樣渴望^_^

上節課的課後習題我們試圖模擬一個場景,裡邊有一隻烏龜和十條魚,烏龜通過吃魚來補充體力,當烏龜體力消耗殆盡或者魚吃光時遊戲結束。
現在我們想擴展遊戲,給魚類進行細分,有金魚(Goldfish),鯉魚(Carp),三文魚(Salmon)和鯊魚(Shark)。那麼我們就在思考一個問題:能不能不要每次都重頭到尾重新定義一個新的魚類呢?因為我們知道大部分魚的屬性和方法是相似的。如果說有一種機制可以讓這些相似的東西得以自動傳遞,我們不用每次都自己動手寫,那就方便快捷的多了。這種機制就是我們今天要講解的繼承。
語法:class 類名(父類名):
被繼承類被稱為基類、父類或超類,繼承者被稱為子類,一個子類可以繼承它的父類的任何屬性和方法。舉例說明:
  1. >>> class Parent:
  2. def hello(self):
  3. print("正在调用父类的方法")
  4. >>> class Child(Parent):
  5. pass
  6. >>> p = Parent()
  7. >>> p.hello()
  8. 正在调用父类的方法
  9. >>> c = Child()
  10. >>> c.hello()
  11. 正在调用父类的方法
需要注意的是:如果子類中定義與父類同名的方法或屬性,則會自動覆蓋父類對應的方法和屬性。
  1. >>> class Parent:
  2. def hello(self):
  3. print("正在调用父类的方法")
  4. >>> class Child(Parent):
  5. def hello(self):
  6. print("正在调用子类的方法")
  7. >>> c = Child()
  8. >>> c.hello()
  9. 正在调用子类的方法
  10. >>> p = Parent()
  11. >>> p.hello()
  12. 正在调用父类的方法
但是,這裡覆蓋的是子類實例化對象裡面的方法而已,對父類的方法沒有影響。
現在大家一起動手寫一下剛才提到的魚類擴展的例子。
這是一個有錯誤的程序代碼:
  1. import random as r
  2. class Fish:
  3. def __init__(self):
  4. self.x = r.randint(0, 10)
  5. self.y = r.randint(0, 10)
  6. def move(self):
  7. self.x -= 1
  8. print("我的位置是:", self.x, self.y)
  9. class Goldfish(Fish):
  10. pass
  11. class Shark(Fish):
  12. def __init__(self):
  13. self.hungry = True
  14. def eat(self):
  15. if self.hungry:
  16. print("吃货的梦想就是天天有吃的^_^")
  17. self.hungry = False
  18. else:
  19. print("太撑了,吃不下了")
但是上面的程序是存在錯誤的。如下:
  1. >>> fish = Fish()
  2. >>> fish.move()
  3. 我的位置是: 2 9
  4. >>> goldfish = Goldfish()
  5. >>> goldfish.move()
  6. 我的位置是: -1 7
  7. >>> shark = Shark()
  8. >>> shark.eat()
  9. 吃货的梦想就是天天有吃的^_^
  10. >>> shark.move()
  11. Traceback (most recent call last):
  12. File "<pyshell#55>", line 1, in <module>
  13. shark.move()
  14. File "C:/Users/XiangyangDai/Desktop/上课代码/38-1.py", line 7, in move
  15. self.x -= 1
  16. AttributeError: 'Shark' object has no attribute 'x'
我們在調用shark.move()方法的時候會報錯,這是為什麼呢?錯誤信息告訴我們,Shark 對像沒有x 的屬性,Goldfish 和Shark 都是繼承Fish,Fish是有x的屬性的,但是Shark重寫了__init__(self)方法,子類重寫了父類的方法,就會把父類的方法給覆蓋。要解決這個問題的話,我們應該在Shark 的類裡面重寫__init__(self)方法的時候先調用父類的__init__(self),實現這樣子的繼承總共有兩種技術。
  • 調用未綁定的父類方法(該方法不重要)
  1. import random as r
  2. class Fish:
  3. def __init__(self):
  4. self.x = r.randint(0, 10)
  5. self.y = r.randint(0, 10)
  6. def move(self):
  7. self.x -= 1
  8. print("我的位置是:", self.x, self.y)
  9. class Goldfish(Fish):
  10. pass
  11. class Shark(Fish):
  12. def __init__(self):
  13. #调用未绑定的父类方法
  14. Fish.__init__(self)
  15. self.hungry = True
  16. def eat(self):
  17. if self.hungry:
  18. print("吃货的梦想就是天天有吃的^_^")
  19. self.hungry = False
  20. else:
  21. print("太撑了,吃不下了")
  1. >>> shark = Shark()
  2. >>> shark.move()
  3. 我的位置是: 6 0
這樣就不會報錯了,需要注意的是, Fish.__init__(self) 中的self 是調用它的父類的方法,但是這個self 是子類的實例對象。
就相當於:Fish.__init__(shark)。實際上,在上面出錯的程序代碼運行之後,我們輸入下面的語句可是可以的:這裡就相當於重新進行了一次初始化。
  1. >>> shark = Shark()
  2. >>> Fish.__init__(shark)
  3. >>> shark.move()
  4. 我的位置是: 6 1
  • 使用super 函數(完美方法)
使用super 函數能夠幫我們自動找到父類的方法,而且還會為我們傳入self 參數,super 函數可以完美的替換上述的方法。
  1. import random as r
  2. class Fish:
  3. def __init__(self):
  4. self.x = r.randint(0, 10)
  5. self.y = r.randint(0, 10)
  6. def move(self):
  7. self.x -= 1
  8. print("我的位置是:", self.x, self.y)
  9. class Goldfish(Fish):
  10. pass
  11. class Shark(Fish):
  12. def __init__(self):
  13. #使用super函数
  14. super().__init__()
  15. self.hungry = True
  16. def eat(self):
  17. if self.hungry:
  18. print("吃货的梦想就是天天有吃的^_^")
  19. self.hungry = False
  20. else:
  21. print("太撑了,吃不下了")
  1. >>> shark = Shark()
  2. >>> shark.move()
  3. 我的位置是: 6 2
super().__init__(),super 函數的超級之處就在於你不用給定任何父類的名字,如果繼承有多重繼承或者父類的名字太過複雜的時候,也不用給出父類的名字,就可以自動幫你一層一層的找出它所有父類裡面對應的方法,由於你不需要給出父類的名字,也就意味著如果你要改變類的繼承關係,你只需要修改class Shark(Fish): 裡面的父類的名字即可。
多重繼承:就是同時繼承多個父類的屬性和方法。
語法:class 類名(父類1名,父類2名........):
  1. >>> class Base1:
  2. def foo1(self):
  3. print("我是foo1,我为Base1代言...")
  4. >>> class Base2:
  5. def foo2(self):
  6. print("我是foo2,我为Base2代言...")
  7. >>> class C(Base1, Base2):
  8. pass
  9. >>> c = C()
  10. >>> c.foo1()
  11. 我是foo1,我为Base1代言...
  12. >>> c.foo2()
  13. 我是foo2,我为Base2代言...
多重繼承可以同時繼承多個父類的屬性和方法,但是多重繼承很容易導致代碼混亂,所以當你不確定你真的必須要使用多重繼承的時候,請盡量避免使用

Po5WZ-#H<k+=UFLu ~G2s^'

0 留言:

發佈留言