什么是繼承
繼承是一種創(chuàng)建新類的方式,新建的類可以繼承一個或多個父類(python支持多繼承),父類又可稱為基類或超類,新建的類稱為派生類或子類。
子類會“”遺傳”父類的屬性,從而解決代碼重用問題(比如練習7中Garen與Riven類有很多冗余的代碼)
python中類的繼承分為:單繼承和多繼承
class
ParentClass1
:
#定義父類
pass
class
ParentClass2
:
#定義父類
pass
class
SubClass1
(
ParentClass1
)
:
#單繼承,基類是ParentClass1,派生類是SubClass
pass
class
SubClass2
(
ParentClass1
,
ParentClass2
)
:
#python支持多繼承,用逗號分隔開多個繼承的類
pass
查看繼承
>>
>
SubClass1
.
__bases__
#__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類
(
<
class
'__main__.ParentClass1'
>
,
)
>>
>
SubClass2
.
__bases__
(
<
class
'__main__.ParentClass1'
>
,
<
class
'__main__.ParentClass2'
>
)
繼承與抽象(先抽象再繼承)
繼承描述的是子類與父類之間的關系,是一種什么是什么的關系。要找出這種關系,必須先抽象再繼承
抽象即抽取類似或者說比較像的部分。
抽象分成兩個層次:
1 將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2 將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關注點,降低復雜度)
繼承:是基于抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
如果我們定義了一個類A,然后又想新建立另外一個類B,但是類B的大部分內容與類A的相同時
我們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
通過繼承的方式新建類B,==讓B繼承A,B會‘遺傳’A的所有屬性(數據屬性和函數屬性),==實現代碼重用
class
Hero
:
def
__init__
(
self
,
nickname
,
aggressivity
,
life_value
)
:
self
.
nickname
=
nickname
self
.
aggressivity
=
aggressivity
self
.
life_value
=
life_value
def
move_forward
(
self
)
:
print
(
'%s move forward'
%
self
.
nickname
)
def
move_backward
(
self
)
:
print
(
'%s move backward'
%
self
.
nickname
)
def
move_left
(
self
)
:
print
(
'%s move forward'
%
self
.
nickname
)
def
move_right
(
self
)
:
print
(
'%s move forward'
%
self
.
nickname
)
def
attack
(
self
,
enemy
)
:
enemy
.
life_value
-=
self
.
aggressivity
class
Garen
(
Hero
)
:
pass
class
Riven
(
Hero
)
:
pass
g1
=
Garen
(
'草叢倫'
,
100
,
300
)
r1
=
Riven
(
'銳雯雯'
,
57
,
200
)
print
(
g1
.
life_value
)
r1
.
attack
(
g1
)
print
(
g1
.
life_value
)
'''
運行結果
'''
注意:像g1.life_value之類的屬性引用,會先從實例中找life_value然后去類中找,然后再去父類中找…直到最頂級的父類。
派生
當然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會影響到父類), 需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調用新增的屬性時,就以自己為準了。
class
Riven
(
Hero
)
:
camp
=
'Noxus'
def
attack
(
self
,
enemy
)
:
#在自己這里定義新的attack,不再使用父類的attack,且不會影響父類
print
(
'from riven'
)
def
fly
(
self
)
:
#在自己這里定義新的
print
(
'%s is flying'
%
self
.
nickname
)
在子類中,新建的重名的函數屬性,在編輯函數內功能的時候,有可能需要重用父類中重名的那個函數功能,應該是用調用普通函數的方式,即:類名.func(),此時就與調用普通函數無異了,因此即便是self參數也要為其傳值
class
Riven
(
Hero
)
:
camp
=
'Noxus'
def
__init__
(
self
,
nickname
,
aggressivity
,
life_value
,
skin
)
:
Hero
.
__init__
(
self
,
nickname
,
aggressivity
,
life_value
)
#調用父類功能
self
.
skin
=
skin
#新屬性
def
attack
(
self
,
enemy
)
:
#在自己這里定義新的attack,不再使用父類的attack,且不會影響父類
Hero
.
attack
(
self
,
enemy
)
#調用功能
print
(
'from riven'
)
def
fly
(
self
)
:
#在自己這里定義新的
print
(
'%s is flying'
%
self
.
nickname
)
r1
=
Riven
(
'銳雯雯'
,
57
,
200
,
'比基尼'
)
r1
.
fly
(
)
print
(
r1
.
skin
)
'''
運行結果
銳雯雯 is flying
比基尼
'''
組合與繼承都是有效地利用已有類的資源的重要方式。但是二者的概念和使用場景皆不同,
1.繼承的方式
通過繼承建立了派生類與基類之間的關系,它是一種’是’的關系,比如白馬是馬,人是動物。
當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學生是人
2.組合的方式
用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python和linux課程,教授有學生s1、s2、s3…
class
People
:
def
__init__
(
self
,
name
,
age
,
sex
)
:
self
.
name
=
name
self
.
age
=
age
self
.
sex
=
sex
class
Course
:
def
__init__
(
self
,
name
,
period
,
price
)
:
self
.
name
=
name
self
.
period
=
period
self
.
price
=
price
def
tell_info
(
self
)
:
print
(
'<%s %s %s>'
%
(
self
.
name
,
self
.
period
,
self
.
price
)
)
class
Teacher
(
People
)
:
def
__init__
(
self
,
name
,
age
,
sex
,
job_title
)
:
People
.
__init__
(
self
,
name
,
age
,
sex
)
self
.
job_title
=
job_title
self
.
course
=
[
]
self
.
students
=
[
]
class
Student
(
People
)
:
def
__init__
(
self
,
name
,
age
,
sex
)
:
People
.
__init__
(
self
,
name
,
age
,
sex
)
self
.
course
=
[
]
egon
=
Teacher
(
'egon'
,
18
,
'male'
,
'沙河霸道金牌講師'
)
s1
=
Student
(
'牛榴彈'
,
18
,
'female'
)
python
=
Course
(
'python'
,
'3mons'
,
3000.0
)
linux
=
Course
(
'lunix'
,
'3mons'
,
3000.0
)
#為老師egon和學生s1添加課程
egon
.
course
.
append
(
python
)
egon
.
course
.
append
(
linux
)
s1
.
course
.
append
(
python
)
#為老師egon添加學生s1
egon
.
students
.
append
(
s1
)
#使用
for
obj
in
egon
.
course
:
obj
.
tell_info
(
)
#輸出:
#
#
接口與歸一化設計
什么是接口
==
==
==
==
==
==
==
==
=
第一部分:Java 語言中的接口很好的展現了接口的含義
:
IAnimal
.
java
/*
* Java的Interface接口的特征:
* 1)是一組功能的集合,而不是一個功能
* 2)接口的功能用于交互,所有的功能都是public,即別的對象可操作
* 3)接口只定義函數,但不涉及函數實現
* 4)這些功能是相關的,都是動物相關的功能,但光合作用就不適宜放到IAnimal里面了 */
package
com
.
oo
.
demo
;
public
interface
IAnimal
{
public
void
eat
(
)
;
public
void
run
(
)
;
public
void
sleep
(
)
;
public
void
speak
(
)
;
}
==
==
==
==
==
==
==
==
=
第二部分:Pig
.
java:豬”的類設計
,
實現了IAnnimal接口
package
com
.
oo
.
demo
;
public
class
Pig
implements
IAnimal
{
//如下每個函數都需要詳細實現
public
void
eat
(
)
{
System
.
out
.
println
(
"Pig like to eat grass"
)
;
}
public
void
run
(
)
{
System
.
out
.
println
(
"Pig run: front legs, back legs"
)
;
}
public
void
sleep
(
)
{
System
.
out
.
println
(
"Pig sleep 16 hours every day"
)
;
}
public
void
speak
(
)
{
System
.
out
.
println
(
"Pig can not speak"
)
;
}
}
==
==
==
==
==
==
==
==
=
第三部分:Person2
.
java
/*
*實現了IAnimal的“人”,有幾點說明一下:
* 1)同樣都實現了IAnimal的接口,但“人”和“豬”的實現不一樣,為了避免太多代碼導致影響閱讀,這里的代碼簡化成一行,但輸出的內容不一樣,實際項目中同一接口的同一功能點,不同的類實現完全不一樣
* 2)這里同樣是“人”這個類,但和前面介紹類時給的類“Person”完全不一樣,這是因為同樣的邏輯概念,在不同的應用場景下,具備的屬性和功能是完全不一樣的 */
package
com
.
oo
.
demo
;
public
class
Person2
implements
IAnimal
{
public
void
eat
(
)
{
System
.
out
.
println
(
"Person like to eat meat"
)
;
}
public
void
run
(
)
{
System
.
out
.
println
(
"Person run: left leg, right leg"
)
;
}
public
void
sleep
(
)
{
System
.
out
.
println
(
"Person sleep 8 hours every dat"
)
;
}
public
void
speak
(
)
{
System
.
out
.
println
(
"Hellow world, I am a person"
)
;
}
}
==
==
==
==
==
==
==
==
=
第四部分:Tester03
.
java
package
com
.
oo
.
demo
;
public
class
Tester03
{
public
static
void
main
(
String
[
]
args
)
{
System
.
out
.
println
(
"===This is a person==="
)
;
IAnimal person
=
new
Person2
(
)
;
person
.
eat
(
)
;
person
.
run
(
)
;
person
.
sleep
(
)
;
person
.
speak
(
)
;
System
.
out
.
println
(
"\n===This is a pig==="
)
;
IAnimal pig
=
new
Pig
(
)
;
pig
.
eat
(
)
;
pig
.
run
(
)
;
pig
.
sleep
(
)
;
pig
.
speak
(
)
;
}
}
為何要用接口
接口提取了一群類共同的函數,可以把接口當做一個函數的集合。
然后讓子類去實現接口中的函數。
這么做的意義在于歸一化,什么叫歸一化,就是只要是基于同一個接口實現的類,那么所有的這些類產生的對象在使用時,從用法上來說都一樣。
繼承的兩種用途
一:繼承基類的方法,并且做出自己的改變或者擴展(代碼重用):實踐中,繼承的這種用途意義并不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。
二:聲明某個子類兼容于某基類,定義一個接口類(模仿java的Interface),接口類中定義了一些接口名(就是函數名)且并未實現接口的功能,子類繼承接口類,并且實現接口中的功能
#_*_coding:utf-8_*_
__author__
=
'Linhaifeng'
#一切皆文件
import
abc
#利用abc模塊實現抽象類
class
All_file
(
metaclass
=
abc
.
ABCMeta
)
:
all_type
=
'file'
@abc
.
abstractmethod
#定義抽象方法,無需實現功能
def
read
(
self
)
:
'子類必須定義讀功能'
pass
@abc
.
abstractmethod
#定義抽象方法,無需實現功能
def
write
(
self
)
:
'子類必須定義寫功能'
pass
# class Txt(All_file):
# pass
#
# t1=Txt() #報錯,子類沒有定義抽象方法
class
Txt
(
All_file
)
:
#子類繼承抽象類,但是必須定義read和write方法
def
read
(
self
)
:
print
(
'文本數據的讀取方法'
)
def
write
(
self
)
:
print
(
'文本數據的讀取方法'
)
class
Sata
(
All_file
)
:
#子類繼承抽象類,但是必須定義read和write方法
def
read
(
self
)
:
print
(
'硬盤數據的讀取方法'
)
def
write
(
self
)
:
print
(
'硬盤數據的讀取方法'
)
class
Process
(
All_file
)
:
#子類繼承抽象類,但是必須定義read和write方法
def
read
(
self
)
:
print
(
'進程數據的讀取方法'
)
def
write
(
self
)
:
print
(
'進程數據的讀取方法'
)
wenbenwenjian
=
Txt
(
)
yingpanwenjian
=
Sata
(
)
jinchengwenjian
=
Process
(
)
#這樣大家都是被歸一化了,也就是一切皆文件的思想
wenbenwenjian
.
read
(
)
yingpanwenjian
.
write
(
)
jinchengwenjian
.
read
(
)
print
(
wenbenwenjian
.
all_type
)
print
(
yingpanwenjian
.
all_type
)
print
(
jinchengwenjian
.
all_type
)
抽象類與接口
抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。
繼承實現的原理
class
A
(
object
)
:
def
test
(
self
)
:
print
(
'from A'
)
class
B
(
A
)
:
def
test
(
self
)
:
print
(
'from B'
)
class
C
(
A
)
:
def
test
(
self
)
:
print
(
'from C'
)
class
D
(
B
)
:
def
test
(
self
)
:
print
(
'from D'
)
class
E
(
C
)
:
def
test
(
self
)
:
print
(
'from E'
)
class
F
(
D
,
E
)
:
# def test(self):
# print('from F')
pass
f1
=
F
(
)
f1
.
test
(
)
print
(
F
.
__mro__
)
#只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性
#新式類繼承順序:F->D->B->E->C->A
#經典類繼承順序:F->D->B->A->E->C
#python3中統(tǒng)一都是新式類
#pyhon2中才分新式類與經典類
繼承順序
子類**中**調用父類的方法
方法一:指名道姓,即父類名.父類方法()
#_*_coding:utf-8_*_
class
Vehicle
:
#定義交通工具類
Country
=
'China'
def
__init__
(
self
,
name
,
speed
,
load
,
power
)
:
self
.
name
=
name
self
.
speed
=
speed
self
.
load
=
load
self
.
power
=
power
def
run
(
self
)
:
print
(
'開動啦...'
)
class
Subway
(
Vehicle
)
:
#地鐵
def
__init__
(
self
,
name
,
speed
,
load
,
power
,
line
)
:
Vehicle
.
__init__
(
self
,
name
,
speed
,
load
,
power
)
self
.
line
=
line
def
run
(
self
)
:
print
(
'地鐵%s號線歡迎您'
%
self
.
line
)
Vehicle
.
run
(
self
)
line13
=
Subway
(
'中國地鐵'
,
'180m/s'
,
'1000人/箱'
,
'電'
,
13
)
line13
.
run
(
)
#輸出:
#地鐵13號線歡迎您
#開動啦...
方法二:super()
class
Vehicle
:
# 定義交通工具類
Country
=
'China'
def
__init__
(
self
,
name
,
speed
,
load
,
power
)
:
self
.
name
=
name
self
.
speed
=
speed
self
.
load
=
load
self
.
power
=
power
def
run
(
self
)
:
print
(
'開動啦...'
)
class
Subway
(
Vehicle
)
:
# 地鐵
def
__init__
(
self
,
name
,
speed
,
load
,
power
,
line
)
:
# super(Subway,self) 就相當于實例本身 在python3中super()等同于super(Subway,self)
super
(
)
.
__init__
(
name
,
speed
,
load
,
power
)
self
.
line
=
line
def
run
(
self
)
:
print
(
'地鐵%s號線歡迎您'
%
self
.
line
)
super
(
)
.
run
(
)
#super()也可以傳入參數
class
Mobike
(
Vehicle
)
:
# 摩拜單車
pass
line13
=
Subway
(
'中國地鐵'
,
'180m/s'
,
'1000人/箱'
,
'電'
,
13
)
line13
.
run
(
)
強調:二者使用哪一種都可以,但最好不要混合使用
指名道姓與super()的區(qū)別
#指名道姓
class
A
:
def
__init__
(
self
)
:
print
(
'A的構造方法'
)
class
B
(
A
)
:
def
__init__
(
self
)
:
print
(
'B的構造方法'
)
A
.
__init__
(
self
)
class
C
(
A
)
:
def
__init__
(
self
)
:
print
(
'C的構造方法'
)
A
.
__init__
(
self
)
class
D
(
B
,
C
)
:
def
__init__
(
self
)
:
print
(
'D的構造方法'
)
B
.
__init__
(
self
)
C
.
__init__
(
self
)
pass
f1
=
D
(
)
#A.__init__被重復調用
'''
D的構造方法
B的構造方法
A的構造方法
C的構造方法
A的構造方法
'''
#使用super()
class
A
:
def
__init__
(
self
)
:
print
(
'A的構造方法'
)
class
B
(
A
)
:
def
__init__
(
self
)
:
print
(
'B的構造方法'
)
super
(
B
,
self
)
.
__init__
(
)
class
C
(
A
)
:
def
__init__
(
self
)
:
print
(
'C的構造方法'
)
super
(
C
,
self
)
.
__init__
(
)
class
D
(
B
,
C
)
:
def
__init__
(
self
)
:
print
(
'D的構造方法'
)
super
(
D
,
self
)
.
__init__
(
)
f1
=
D
(
)
#super()會基于mro列表,往后找
'''
D的構造方法
B的構造方法
C的構造方法
A的構造方法
'''
當你使用super()函數時,Python會在MRO列表上繼續(xù)搜索下一個類。只要每個重定義的方法統(tǒng)一使用super()并只調用它一次,那么控制流最終會遍歷完整個MRO列表,每個方法也只會被調用一次(注意注意注意:使用super調用的所有屬性,都是從MRO列表當前的位置往后找,千萬不要通過看代碼去找繼承關系,一定要看MRO列表)
轉:https://www.cnblogs.com/linhaifeng/articles/7340153.html
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
