Python3 與 C# 面向對象之~繼承與多態

 

2.繼承

代碼褲子:https://github.com/lotapp/BaseCodehtml

在線編程:https://mybinder.org/v2/gh/lotapp/BaseCode/masterpython

在線預覽:http://github.lesschina.com/python/base/oop/2.繼承與多態.htmlgit

2.1.單繼承

在OOP中,當咱們定義一個Class的時候,能夠從某個現有的Class繼承github

新的Class稱爲子類,而被繼承的class稱爲 基類 或者 父類編程

Python的繼承格式 ==> xxx(base_class)設計模式

小明興高采烈的聽着老師開新課,不一會就看見了一個演示Demo:app

In [1]:
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

In [2]:
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()
 
1
我是實例方法,動物會吃哦~
動物
我是類方法,動物會叫哦
我是類方法,動物會叫哦
我是類方法,動物會叫哦
我是靜態方法,動物會玩耍哦
我是靜態方法,動物會玩耍哦
我是靜態方法,動物會玩耍哦
 

來張圖就懂了,不是 私有的 都能訪問:ide

一張圖就懂了

這時候,小明老高興了,單回頭一想 ==> 不科學啊,dog應該有其對應的方法吧,C#有虛方法重寫,Python怎麼搞?在子類裏面又怎麼調用父類方法呢?函數

對於小明的提示老師很高興,因而點名小潘來寫一個子類調用父類的demo(老師昨天從窗戶裏看見小潘有預習):

In [3]:
# 調用父類的方法
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給老師:

In [4]:
# 重寫父類方法==>子類和父類有同名方法
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() 這樣,咱們就得到了繼承的另外一個好處:多態

2.2.多繼承

在講多態以前,咱們先引入一下Python的 多繼承 對,你沒有聽錯

Java、C#都是單繼承,多實現。Python和C++同樣,能夠多繼承,先不要吐槽,規範使用其實很方便的

來個案例看看:

In [5]:
# 多繼承引入
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

In [6]:
# 若是父類裏面有同名方法怎麼知道調哪一個?
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()
 
開心的吃飯
(<class '__main__.Son'>, <class '__main__.Mom'>, <class '__main__.Father'>, <class 'object'>)
 

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個子類,那每一個子類裏面都有一個父類方法,想一想這是多麼浪費的一件事情?


2.3.C#繼承

下課後,小明認真思考總結,而後對照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

2.4C#接口的多實現

定義兩個接口:

public interface IRun
{
    //什麼都不用加
    void Run();
}

public interface IEat
{
    void Eat();
}

定義一個Dog類來實現兩個接口,這樣dog就有了run和eat的方法了

var dog = new Dog();
dog.Eat();
dog.Run();

結果:

狗狗吃
狗狗跑

3 多態

3.1.Python

說多態以前說說類型判斷,之前咱們用type() or isinstance()

判斷一個變量和另外一個變量是不是同一個類型==> type(a)==type(b)

判斷一個變量是不是某個類型==> type(a)==A or isinstance(a,A)

In [7]:
# 判斷一個變量是不是某個類型 ==> 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()
 
True
True
<class '__main__.Dog'>
True
True
 

小明老高興了,終於講解多態了,不由問道:「多態的好處是啥?」

小潘瞥了一眼小明~「廢話,確定爲了 屏蔽子類差別用的啊,像簡單工廠不就乾的這個事?"

小明楞了楞,眼巴巴的看着老師繼續講課。

設計模式咱們會找個專題講講,如今給大家說的是Python的基礎。

Python是動態語言的「鴨子類型」,它並不要求嚴格的繼承體系。

一個對象只要「看起來像鴨子,走起路來像鴨子」,那它就能夠被看作是鴨子(最後會貼一個案例)

C#實現多態有不少方式,好比虛方法,好比抽象類,好比接口等等...

小明迷迷糊糊的問道:「那 Python怎麼實現多態呢?」

老師看了一眼打斷他講課的小明,而後繼續說道~來個簡單案例:

In [8]:
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這麼寫有個好處哦,咱們來看個案例,而後你本身總結「

In [9]:
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()
 
趕時間的吃飯
優雅的吃飯
舔着吃
 

小明趕忙結合以前學的內容寫了個小案例:

In [10]:
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#版的多態

3.2.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歲了,我是一個靦腆的小女孩
 

3.3.C#抽象類實現多態

定義一個動物類:

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();
}

結果:

汪汪叫~~~
喵喵叫~~~
 

3.4.C#接口實現多態

定義一個跑的接口:

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();
}

結果:

飛快的跑着去上課
飛快的跑着上樹
相關文章
相關標籤/搜索