#4 Python面向對象(三)

前言

前兩節講解了Python面向對象的思想和Python類中各類變量的含義以及區別。確定有小夥伴會問,類初始化時是否能夠傳入參數?若是有多個類中含有共同的函數方法,是否能夠重複利用?本節就帶着這些問題來繼續深刻類。Here We Go!安全

1、類的傳參

1.1 帶參數的初始化

仍是以Doctor類爲例,假如lisi是一位男性醫生,如今要求在生成lisi這個對象時傳入其性別。怎麼辦呢?函數

按照以前所學的,相似於函數的傳參,確定是:ui

lisi = Doctor('male')

的確,類帶參數的初始化就是這麼用的,那麼再按照函數的思想,類源代碼應該這麼寫:spa

class Doctor(name):
    def talk(self):
        print('My gender is {0}'.format(self.name))


lisi = Doctor('male')

lisi.talk()
# 按照函數的思想,建立一個帶參數的類
# 這是錯誤的!!!!

你已經看到,代碼裏已經標出這是錯誤的編寫方法,不信的話來運行一下:code

Traceback (most recent call last):
  File "1.py", line 1, in <module>
    class Doctor(name):
NameError: name 'name' is not defined


# 居然拋出name未定義異常

這是爲何呢?莫名其妙的居然拋出未定義異常,關於這個問題將會放到下面(類的性質)來說orm

那帶參數的類要怎麼編寫呢?使用特殊方法 __init__(注意:init左右兩邊都是兩個下劃線) ,先來看個例子:對象

class Doctor():
    def __init__(self, name):
    ¦   self.name = name

    def talk(self):
    ¦   print('My gender is {0}'.format(self.name))


lisi = Doctor('male')

lisi.talk()

# 運行結果:
My gender is male

經過上面的代碼能夠看出,要想建立一個帶參數的類,類裏面要建立一個__init__的方法,其實這個特殊的函數叫作構造函數,構造函數會在實例化類後默默運行一次,也就是說,只要實例化了一個類,那麼就已經運行了構造函數。構造函數一般用來傳入參數使用。以上就是類的傳參。blog

1.2 類實例化後運行順序

當一個類實例化後,類中的代碼被首先運行,其次是構造函數裏的代碼,再而後是被調用的函數被運行,最後是析構函數被運行(析構函數將放在類的特殊方法講)繼承

class Doctor():
    def __init__(self, name):
    ¦   print('我是第二個被運行的')
    ¦   self.name = name

    print('先運行我')

    def talk(self):
    ¦   print('只有調用我時才運行我')


lisi = Doctor('male')

lisi.talk()


# 運行結果:
先運行我
我是第二個被運行的
只有調用我時才運行我

從上面的代碼中能夠清晰的看到每一個代碼塊的運行順序接口

2、面向對象的性質

面向對象共有三大性質:封裝性、繼承性、多態性。這三大性質是必定要記住的,不只要記住,更要理解它們👹

2.1 封裝性

先舉個栗子🌰哇,一所學校有教學樓、圖書館、宿舍樓,你做爲這個學校的學生,你固然能夠隨意使用這三個地點,可是外來人員恐怕就不能使用了,學校阻止外來人員的辦法是創建圍牆,將學校圍起來。這就是封裝的含義,創建圍牆圍學校(類)就是封裝,只有學生(類的對象)可使用學校資源,這兩點加起來就是封裝性。

封裝性概念:把客觀事物封裝成抽象的類,而且類能夠把本身的數據和方法只讓可信的類或者對象操做,對不可信的進行信息隱藏。對內部數據進行了保護,防止程序其餘部分使用或修改內部數據

總的來講,封裝性就是安全➕自私🤭

2.2 繼承性

還記得前言中帶的問題嗎?當多個類擁有一樣的方法時,是否能夠只寫一次重複利用,這就是繼承的優點。

繼承繼承,顧名思義,就是繼承🤥,父親和兒子嘛

繼承性:它可使用現有類的全部功能,並在無需從新編寫原來的類的狀況下對這些功能進行擴展。

繼承者稱爲:子類、派生類;被繼承者稱爲:基類、父類、超類;在Python中,被繼承者更習慣稱爲超類

說了這麼多枯燥難以理解的概念,其實只須要一些例子就夠了:

class Animal:
    '''
    全部動物的超類
    '''
    def eat(self):
    ¦   print('動物都會吃飯')

    def talk(self):
    ¦   print('動物都會叫')


class Dog(Animal):
    '''
    繼承Animal
    '''
    def smell(self):
    ¦   print('狗的嗅覺靈敏')


class Cat(Animal):
    '''
    繼承Animal
    '''
    def tree(self):
    ¦   print('貓會爬樹')

從上面的代碼能夠看到,原來 類(超類) 括號裏面時超類啊,不是指參數。調用的例子:

dog = Dog()  # 實例化dog
cat = Cat()  # 實例化cat

dog.eat()  # dog調用了超類Animal的方法
dog.talk()
dog.smell()  # dog調用本身的smell方法

cat.eat()  # cat也是同樣的
cat.talk()
cat.tree()
# 運行結果:
動物都會吃飯
動物都會叫
狗的嗅覺靈敏
動物都會吃飯
動物都會叫
貓會爬樹

經過繼承,能夠大大增長代碼的利用率。

人類社會中,繼承每每是長江後浪推前浪,一浪更比一浪強!Python中的子類固然也能夠將超類拍死在沙灘上🥴,那就是改寫超類方法方便本身

2.2.1 繼承性——改寫不帶參數的超類

class Animal:
    '''
    全部動物的超類
    '''

    def eat(self):
    ¦   print('動物都會吃飯')

    def talk(self):
    ¦   print('動物都會叫')


class Dog(Animal):
    '''
    繼承Animal
    '''

    def smell(self):
    ¦   print('狗的嗅覺靈敏')

    def eat(self):
    ¦   '''
    ¦   改寫超類eat方法
    ¦   '''
    ¦   print('吃肉!')


dog = Dog()  # 實例化dog

dog.talk()
dog.smell()  # dog調用本身的smell方法
dog.eat()  # 調用被改寫的eat方法
# 運行結果:
動物都會叫
狗的嗅覺靈敏
吃肉!

2.2.2 繼承性——改寫帶參數的超類

超類帶參數,子類不帶參數

    '''
    全部動物的超類
    '''

    def __init__(self, name, age):
    ¦   self.name = name
    ¦   self.age = age

    def eat(self):
    ¦   print('動物都會吃飯')

    def talk(self):
    ¦   print('動物都會叫')


class Dog(Animal):
    '''
    繼承Animal
    '''

    def smell(self):
    ¦   print('狗的嗅覺靈敏')

    def eat(self):
    ¦   '''
    ¦   改寫超類eat方法
    ¦   '''
    ¦   print('吃肉!')


dog = Dog()  # 實例化dog

dog.eat()

# 運行結果:

Traceback (most recent call last):
File "6.py", line 32, in <module>
dog = Dog() # 實例化dog
TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'

運行上述代碼,會拋出name,age未定義異常,也就是說,當超類帶有參數時,子類實例化時也要參入超類的參數:

dog = Dog('wangwang',5)  # 實例化dog,傳入超類須要的參數

dog.eat()


# 運行結果:
吃肉!

 超類帶參數,子類帶參數

class Animal:
    '''
    全部動物的超類
    '''

    def __init__(self, name, age):
    ¦   self.name = name
    ¦   self.age = age

    def eat(self):
    ¦   print('動物都會吃飯')

    def talk(self):
    ¦   print('動物都會叫')


class Dog(Animal):
    '''
    繼承Animal
    '''

    def __init__(self, gender):
    ¦   self.gender = gender

    def smell(self):
    ¦   print('狗的嗅覺靈敏')

    def eat(self):
    ¦   '''
    ¦   改寫超類eat方法
    ¦   '''
    ¦   print('吃肉!')

看到這裏,你確定會有疑問🤔️,傳入幾個參數纔會正確呢?超類須要兩個,子類須要一個,那就是三個嘍~~~(天真🙂)

dog = Dog('wangwang',5, 'male')  # 實例化dog

dog.eat()


# 運行結果:
Traceback (most recent call last):
  File "7.py", line 35, in <module>
    dog = Dog('wangwang',5, 'male')  # 實例化dog
TypeError: __init__() takes 2 positional arguments but 4 were given

根據異常信息可知,__init__()須要兩個參數,可是給了4個。

?????哪來的4個?????明明只傳入了3個

無論幾個了,既然須要兩個參數,那就是隻用傳入超類的參數啦~~~(天真🙂)

dog = Dog('wangwang',5)  # 實例化dog

dog.eat()


# 運行結果:
Traceback (most recent call last):
  File "7.py", line 35, in <module>
    dog = Dog('wangwang',5)  # 實例化dog
TypeError: __init__() takes 2 positional arguments but 3 were given

根據異常信息可知,__init__()須要2個參數,可是給了3個。

?????怎麼又成3個了?????

難不成傳入1個會變成2個?豈不是和2個參數對應了(嘿嘿🙊)

dog = Dog('wangwang')  # 實例化dog

dog.eat()


# 運行結果:
吃肉!

終於正確了,那麼傳入的這個參數究竟是哪一個變量呢?

dog = Dog('wangwang')  # 實例化dog
print(dog.name)
print(dog.age)
print(dog.gender)

# 運行結果:
Traceback (most recent call last):
  File "7.py", line 36, in <module>
    print(dog.name)
AttributeError: 'Dog' object has no attribute 'name'
# 沒有name變量
# 這時應該已經猜出傳入的參數給gender變量了
dog = Dog('wangwang')  # 實例化dog
print(dog.gender)
dog.eat()


# 運行結果:
wangwang
吃肉!

能夠看到,的確是傳給了gender變量了,那麼爲何只須要一個參數呢?超類的兩個參數不知去向?其實若是你理解了上一點中的重構方法,就會發現上面的代碼中根本就是重構了超類了__init__()方法,已經和超類中的__init__()無關了🤯

那若是超類的參數和子類的參數都須要呢?Python3中,使用super()方法將超類的構造函數複製到子類中,用法以下:

class Animal:
    '''
    全部動物的超類
    '''

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print('動物都會吃飯')

    def talk(self):
        print('動物都會叫')


class Dog(Animal):
    '''
    繼承Animal
    '''

    def __init__(self, name, age, gender):
        super().__init__(name, age)  # 使用super方法
        self.gender = gender

    def smell(self):
        print('狗的嗅覺靈敏')

    def eat(self):
        '''
        改寫超類eat方法
        '''
        print('吃肉!')


dog = Dog('wangwang', 5, 'male')  # 實例化dog
print(dog.name)
print(dog.age)
print(dog.gender)
dog.eat()

# 運行結果:
wangwang
5
male
吃肉!

從上面代碼中能夠看到super()實際上是超類的一個對象,上述代碼的思想是:給子類傳入3個參數,前2個參數再傳給超類,從而將超類須要的參數傳入

2.3 多態性

什麼是多態呢?先舉個例子哇:今天班級大掃除,老師要分配工做,現有1~10共10個學生,老師確定會這樣說:「1,2,3,4,5去掃地,6,7,8,9,10拖地」,絕對不會這樣說:「1去掃地,2去掃地,3去掃地,4去掃地,5去掃地,6去拖地,7去拖地,8去拖地,9去拖地,10去拖地」。這其實就是多態性,掃地是一個接口,多我的共用,拖地時一個接口,多我的共用。在Python中,多態的例子有不少,最多見的恐怕就是len()這個內置函數了,你會發現不管是字符串,仍是列表,或者元組等均可以使用len()來統計長度,相似這種接口複用的方法就體現了多態性。

多態性概念:容許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做

class Animal:
    def __init__(self, name):
    ¦   self.name = name


class Dog(Animal):
    def eat(self):
    ¦   print('{0} eat cat'.format(self.name))


class Cat(Animal):
    def eat(self):
    ¦   print('{0} eat mouse'.format(self.name))


class Mouse(Animal):
    def eat(self):
    ¦   print('{0} eat {0}'.format(self.name))


def eat(obj):   # 一個接口,多種用法
    obj.eat()


dog = Dog('')
cat = Cat('')
mouse = Mouse('老鼠')

eat(dog)
eat(cat)
eat(mouse)
# 運行結果:
狗 eat cat
貓 eat mouse
老鼠 eat 老鼠

經過以上代碼應該能夠很清晰的瞭解多態性

總結

Python面向對象的知識立刻就要結束了,還剩下特殊方法、舊類和新類的不一樣之處,下次見~

相關文章
相關標籤/搜索