代碼褲子:https://github.com/lotapp/BaseCodehtml
在線編程:https://mybinder.org/v2/gh/lotapp/BaseCode/masterpython
在線預覽:http://github.lesschina.com/python/base/oop/2.繼承與多態.htmlgit
在OOP中,當咱們定義一個Class的時候,能夠從某個現有的Class繼承github
新的Class稱爲子類,而被繼承的class稱爲 基類 或者 父類編程
Python的繼承格式 ==> xxx(base_class)設計模式
小明興高采烈的聽着老師開新課,不一會就看見了一個演示Demo:app
class Animal(object):
def eat(self):
print("動物會吃")
class Cat(Animal):
# 注意一下Python的繼承格式
pass
class Dog(Animal):
pass
def main():
cat = Cat()
dog = Dog()
cat.eat()
dog.eat()
if __name__ == "__main__":
main()
當聽到老師說:「私有的屬性方法 不會被子類繼承 」的時候,小明內心一顫,聯想到以前講的 類屬性、實例屬性、實例方法、類方法、靜態方法,因而趕忙寫個Demo驗證一下:less
class Animal(object):
# 類屬性
name = '動物'
def __init__(self):
# 實例屬性
self.age = 1
def __bug(self):
"""實例私有方法"""
print("我是動物類身上的私有方法:bug")
def eat(self):
"""實例方法"""
print("我是實例方法,動物會吃哦~")
@classmethod
def call(cls):
"""類方法"""
print("我是類方法,動物會叫哦")
@staticmethod
def play():
"""靜態方法"""
print("我是靜態方法,動物會玩耍哦")
class Dog(Animal):
pass
def main():
dog = Dog()
# 實例屬性
print(dog.age)
# 實例方法
dog.eat()
# 類屬性
print(dog.name)
# 類方法
dog.call()
Dog.call()
Animal.call()
# 靜態方法
dog.play()
Dog.play()
Animal.play()
if __name__ == '__main__':
main()
來張圖就懂了,不是 私有的 都能訪問:ide
這時候,小明老高興了,單回頭一想 ==> 不科學啊,dog應該有其對應的方法吧,C#有虛方法重寫,Python怎麼搞?在子類裏面又怎麼調用父類方法呢?函數
對於小明的提示老師很高興,因而點名小潘來寫一個子類調用父類的demo(老師昨天從窗戶裏看見小潘有預習):
# 調用父類的方法
class Father(object):
def eat(self):
print("文雅的吃飯")
class Son(Father):
def eat(self):
# 調用父類方法第1種(super().方法)
super().eat()
class GrandSon(Son):
def eat(self):
# 調用父類方法第2種(記得傳self)
Son.eat(self)
def main():
xiaoming = Son()
xiaoming.eat()
xiaoli = GrandSon()
xiaoli.eat()
if __name__ == '__main__':
main()
通常咱們使用super().方法
來調用父類方法
第二種方法類名.方法(self)
千萬別忘記傳self哦
對了,C#是用base關鍵詞,別搞混了
小明這時候可不高興了,風頭怎麼能被小潘所有搶走呢,趕忙問問旁邊一樣預習的偉哥
不一下子淡定的發了份重寫父類方法的demo給老師:
# 重寫父類方法==>子類和父類有同名方法
class Father(object):
def __init__(self, name):
self.name = name
def eat(self):
print("%s喜歡文雅的吃飯" % self.name)
class Son(Father):
def __init__(self, name):
super().__init__(name)
def eat(self):
print("%s喜歡大口吃飯大口喝酒" % self.name)
def main():
xiaoming = Father("小明")
xiaoming.eat()
xiaopan = Son("小潘")
xiaopan.eat()
if __name__ == "__main__":
main()
老師半喜半憂的說道:「小明同窗啊,你也老大不小了,怎麼跟孩子同樣啊?案例不錯,可是怎麼能人身攻擊人家小潘了?」
當子類和父類都存在相同的eat()
方法時,咱們說,子類的eat()
覆蓋了父類的eat()
在代碼運行的時候,老是會調用子類的eat()
這樣,咱們就得到了繼承的另外一個好處:多態
在講多態
以前,咱們先引入一下Python的 多繼承 對,你沒有聽錯
Java、C#都是單繼承,多實現。Python和C++同樣,能夠多繼承,先不要吐槽,規範使用其實很方便的
來個案例看看:
# 多繼承引入
class Father(object):
def eat(self):
print("文雅的吃飯")
class Mom(object):
def run(self):
print("小碎步")
class Son(Father, Mom):
pass
def main():
son = Son()
son.eat()
son.run()
if __name__ == '__main__':
main()
繼承能夠把父類的全部功能都直接拿過來,這樣就沒必要重0開始寫代碼,子類只須要新增本身特有的方法,也能夠把父類不適合的方法覆蓋重寫
注意一個狀況,若是父類裏面有同名方法咋辦了?到底調哪一個呢?
使用子類名.__mro__
能夠看在調方法的時候搜索順序
通常同名方法都是 先看本身有沒有,而後看繼承順序,好比這邊 先看Mom再看Father
# 若是父類裏面有同名方法怎麼知道調哪一個?
class Father(object):
def eat(self):
print("文雅的吃飯")
class Mom(object):
def eat(self):
print("開心的吃飯")
class Son(Mom, Father):
pass
def main():
son = Son()
son.eat()
print(Son.__mro__) # 通常同名方法都是先看本身有沒有,而後看繼承順序,好比這邊先看Mom再看Father
if __name__ == '__main__':
main()
Python的多繼承最好是當C#或者Java裏面的接口使用,這樣結構不會混亂(特殊狀況除外)
來個例子:
class Animal(object):
pass
class Flyable(object):
"""飛的方法"""
pass
class Runable(object):
"""跑的方法"""
pass
class Dog(Animal, Runable):
pass
class Cat(Animal, Runable):
pass
class Bird(Animal, Flyable):
pass
class Dack(Animal, Runable, Flyable):
"""鴨子會飛也會跑"""
pass
和C#同樣,Python的 父類構造函數不會被繼承
其實從資源角度也不該該被繼承,若是有1w個子類,那每一個子類裏面都有一個父類方法,想一想這是多麼浪費的一件事情?
下課後,小明認真思考總結,而後對照Python寫下了C#版的繼承:
定義一我的類
public class Person
{
public string Name { get; set; }
public ushort Age { get; set; }
public Person(string name, ushort age)
{
this.Name = name;
this.Age = age;
}
public void Hi()//People
{
Console.WriteLine("Name: " + this.Name + " Age: " + this.Age);
}
public virtual void Show()//People
{
Console.WriteLine("Name: " + this.Name + " Age: " + this.Age);
}
}
定義一個學生類
public class Student : Person
{
#region 屬性
/// <summary>
/// 學校
/// </summary>
public string School { get; set; }
/// <summary>
/// 班級
/// </summary>
public string StrClass { get; set; }
/// <summary>
/// 學號
/// </summary>
public string StrNum { get; set; }
#endregion
#region 構造函數
/// <summary>
/// 調用父類構造函數
/// </summary>
/// <param name="name"></param>
/// <param name="age"></param>
public Student(string name, ushort age) : base(name, age)
{
}
public Student(string name, ushort age, string school, string strClass, string strNum) : this(name, age)
{
this.School = school;
this.StrClass = strClass;
this.StrNum = strNum;
}
#endregion
/// <summary>
/// new-隱藏
/// </summary>
public new void Hi()//Student
{
Console.WriteLine("Name: " + this.Name + " Age: " + this.Age + " School: " + this.School + " strClass: " + this.StrClass + " strNum: " + this.StrNum);
}
/// <summary>
/// override-覆蓋
/// </summary>
public override void Show()//Student
{
Console.WriteLine("Name: " + this.Name + " Age: " + this.Age + " School: " + this.School + " strClass: " + this.StrClass + " strNum: " + this.StrNum);
}
}
調用一下:
Person p = new Student("app", 10, "北京大學", "001", "01001");
p.Hi(); p.Show();
Console.WriteLine();
Student s = p as Student;
s.Hi(); s.Show();
結果:
Name: app Age: 10 Name: app Age: 10 School: 北京大學 strClass: 001 strNum: 01001 Name: app Age: 10 School: 北京大學 strClass: 001 strNum: 01001 Name: app Age: 10 School: 北京大學 strClass: 001 strNum: 01001
定義兩個接口:
public interface IRun
{
//什麼都不用加
void Run();
}
public interface IEat
{
void Eat();
}
定義一個Dog類來實現兩個接口,這樣dog就有了run和eat的方法了
var dog = new Dog();
dog.Eat();
dog.Run();
結果:
狗狗吃 狗狗跑
說多態以前說說類型判斷,之前咱們用type()
or isinstance()
判斷一個變量和另外一個變量是不是同一個類型==> type(a)==type(b)
判斷一個變量是不是某個類型==> type(a)==A
or isinstance(a,A)
# 判斷一個變量是不是某個類型 ==> isinstance() or type
class Animal(object):
pass
class Dog(Animal):
pass
def main():
dog = Dog()
dog2 = Dog()
print(type(dog) == Dog)
print(type(dog) == type(dog2))
print(type(dog))
print(isinstance(dog, Dog))
print(isinstance(dog, Animal))
# arg 2 must be a type or tuple
# print(isinstance(dog, dog2))
if __name__ == '__main__':
main()
小明老高興了,終於講解多態了,不由問道:「多態的好處是啥?」
小潘瞥了一眼小明~「廢話,確定爲了 屏蔽子類差別用的啊,像簡單工廠不就乾的這個事?"
小明楞了楞,眼巴巴的看着老師繼續講課。
設計模式咱們會找個專題講講,如今給大家說的是Python的基礎。
Python是動態語言的「鴨子類型」,它並不要求嚴格的繼承體系。
一個對象只要「看起來像鴨子,走起路來像鴨子」,那它就能夠被看作是鴨子(最後會貼一個案例)
C#實現多態有不少方式,好比虛方法,好比抽象類,好比接口等等...
小明迷迷糊糊的問道:「那 Python怎麼實現多態呢?」
老師看了一眼打斷他講課的小明,而後繼續說道~來個簡單案例:
class People(object):
def eat(self):
print("人類會吃飯")
class Father(People):
def eat(self):
print("優雅的吃飯")
class Teacher(People):
def eat(self):
print("趕時間的吃飯")
# C# 或者 Java裏面 寫成 eat(People obj)
def eat(obj):
obj.eat()
def main():
teacher = Teacher()
father = Father()
eat(teacher)
eat(father)
if __name__ == '__main__':
main()
多態的好處在於,若是這時候我再來個Son子類,只要eat()方法編寫正確,不用管原來的代碼是如何調用的
此次小明懂了,爲了裝一下,說道:」老師老師,我記得C# 或者 Java裏面是寫成 eat(People obj) 的吧?「
老師欣慰的笑了一下,說道:」記得剛纔說的填鴨式
嗎?Python這麼寫有個好處哦,咱們來看個案例,而後你本身總結「
class People(object):
def eat(self):
print("人類會吃飯")
class Father(People):
def eat(self):
print("優雅的吃飯")
class Teacher(People):
def eat(self):
print("趕時間的吃飯")
class Dog(object):
def eat(self):
print("舔着吃")
def eat(obj):
obj.eat()
def main():
teacher = Teacher()
father = Father()
eat(teacher)
eat(father)
# 咱們添加一個不是People子類的Dog類,只要有eat方法,參數同樣就能夠直接調
dog = Dog()
eat(dog)
if __name__ == '__main__':
main()
小明趕忙結合以前學的內容寫了個小案例:
class People(object):
def eat(self):
print("人類會吃飯")
class Father(People):
def eat(self):
print("優雅的吃飯")
class Dog(object):
def eat(self):
print("舔着吃飯")
class Factory(object):
@classmethod
def eat(cls, obj):
if hasattr(obj, "eat"):
obj.eat()
else:
raise Exception("該類沒有eat()方法!")
def main():
people = People()
father = Father()
dog = Dog()
Factory.eat(people)
Factory.eat(father)
Factory.eat(dog)
if __name__ == '__main__':
main()
小明忽然大聲說道:」老師老師,我知道了,Python這是吧類的繼承和接口繼承融合起來了啊,實現多態就至關於C#裏面的接口實現多態啊!!!「
老師點評道:」你姑且能夠這麼理解,這些咱們後面還會繼續說的,這種填鴨式的手段剛開始的確會有點不方便,用着用着你就會以爲挺方便的「
小明認真思考總結,而後對照Python和小潘一塊兒寫下了 C#版的多態:
定義一我的類:
public class Person
{
#region 字段+屬性
/// <summary>
/// 姓名
/// </summary>
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
/// <summary>
/// 性別
/// </summary>
private bool _gender;
public bool Gender
{
get
{
return _gender;
}
set
{
_gender = value;
}
}
/// <summary>
/// 年齡
/// </summary>
public short Age { get; set; }
#endregion
#region 構造函數
public Person() { }
public Person(string name, bool gender)
{
this.Name = name;
this.Gender = gender;
}
public Person(string name, bool gender, short age) : this(name, gender)
{
this.Age = age;
}
#endregion
#region 方法
/// <summary>
/// 打招呼
/// </summary>
public virtual void SaiHi()
{
Console.WriteLine("我是一我的類!");
}
#endregion
}
定義一個女孩類:
public class Gril : Person
{
#region 構造函數
public Gril() { }
public Gril(string name, bool gender) : base(name, gender) { }
public Gril(string name, bool gender, short age) : base(name, gender, age) { }
#endregion
/// <summary>
/// 重寫父類方法
/// </summary>
public override void SaiHi()
{
string genderStr = Gender == true ? "男孩" : "女孩";
Console.WriteLine($"你好,我叫{Name},今年{Age}歲了,我是一個靦腆的小{genderStr}");
}
}
定義一個男孩類:
public class Boy : Person
{
#region 構造函數
public Boy() { }
public Boy(string name, bool gender) : base(name, gender) { }
public Boy(string name, bool gender, short age) : base(name, gender, age) { }
#endregion
//public void SaiHi()
public override void SaiHi()
{
string genderStr = Gender == true ? "男孩" : "女孩";
Console.WriteLine($"你好,我叫{Name},今年{Age}歲了,我是一個靦腆的小{genderStr}");
}
}
調用:
static void Main(string[] args)
{
Person[] persons = { new Person(), new Boy("鐵鍋", true, 13), new Gril("妞妞", false, 22) };
foreach (var item in persons)
{
//看看item裏面到底放的是什麼
Console.WriteLine(item.ToString());
item.SaiHi();
Console.WriteLine();
}
}
結果:
Polymorphism1.Person 我是一我的類! Polymorphism1.Boy 你好,我叫鐵鍋,今年13歲了,我是一個靦腆的小男孩 Polymorphism1.Gril 你好,我叫妞妞,今年22歲了,我是一個靦腆的小女孩
定義一個動物類:
public abstract class Animal
{
/// <summary>
/// 抽象類中能夠有正常的方法
/// </summary>
public void Action()
{
Console.WriteLine("動物能夠動");
}
/// <summary>
/// 抽象方法必須在抽象類中
/// </summary>
public abstract void Call();
}
定義一個貓科動物類(子類必須實現父類抽象方法,若是不實現,那麼該類也必須是抽象類)
/// <summary>
/// 貓科動物---子類必須實現父類抽象方法,若是不實現,那麼該類也必須是抽象類
/// </summary>
public abstract class Feline : Animal
{
}
定義一個貓類
public class Cat : Feline
{
/// <summary>
/// 子類必須實現父類抽象方法,若是不實現,那麼該類也必須是抽象類
/// </summary>
public override void Call()
{
Console.WriteLine("喵喵叫~~~");
}
}
定義一個狗類
public class Dog : Animal
{
/// <summary>
/// 子類必須實現抽象類中的抽象方法
/// </summary>
public override void Call()
{
Console.WriteLine("汪汪叫~~~");
}
}
調用:
Animal[] animals = { new Dog(), new Cat() };
foreach (var item in animals)
{
item.Call();
}
結果:
汪汪叫~~~ 喵喵叫~~~
定義一個跑的接口:
public interface IRun
{
/// <summary>
/// 接口中能夠聲明屬性,方法,索引器等
/// </summary>
//string Name { get; set; }
void Runing();
}
定義一個貓類:
public class Cat : IRun
{
public void Runing()
{
Console.WriteLine("飛快的跑着上樹");
}
}
定義一個學生類:
public class Student : IRun
{
public void Runing()
{
Console.WriteLine("飛快的跑着去上課");
}
}
調用:
IRun[] objs = { new Student(), new Cat() };
foreach (var item in objs)
{
item.Runing();
}
結果:
飛快的跑着去上課 飛快的跑着上樹