面向對象三大特性之多態性


一 多態

1、什么是多態

多態指的是同一種事物有多種形態,比如動物有多種形態:貓、狗、豬

class Animal:
    pass

class People(Animal):
    pass

class Dog(Animal):
    pass

class Pig(Animal):
    pass

2、為何要有多態

多態具有多態性,多態性指的是可以在不考慮對象具體類型的情況下而直接使用對象

多態性的好處在於增強了程序的靈活性和可擴展性,比如通過繼承Animal類創建了一個新的類,實例化得到的對象obj,可以使用相同的方式使用obj.talk()

class Animal: # 統一所有子類的方法
    def say(self):
        print('動物基本的發聲頻率。。。',end=' ')

class People(Animal):
    def say(self):
        super().say()
        print('嚶嚶嚶嚶嚶嚶嚶')

class Dog(Animal):
    def say(self):
        super().say()
        print('汪汪汪')

class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')


obj1=People()
obj2=Dog()
obj3=Pig()

obj1.say()
obj2.say()
obj3.say()

因為所有類都具有 say方法,不用管什么對象類型,都可調用say 方法。因此這里可以通過定義接口功能來調用say 方法,使代碼更為簡潔

obj1=People()
obj2=Dog()
obj3=Pig() 

def animal_say(animal):
  	animal.say()
    

animal_say(obj1) #動物基本的發聲頻率。。。 嚶嚶嚶嚶嚶嚶嚶
animal_say(obj2) #動物基本的發聲頻率。。。 汪汪汪
animal_say(obj3) #動物基本的發聲頻率。。。 哼哼哼

案例:Python中一切皆對象,本身就支持多態性

#方式一:都有__len__方法
print('hello'.__len__()) # 5
print([1,2,3].__len__()) #3
print({'a':1,'b':2}.__len__()) #2

#方式二
#定義檢測長度函數
def my_len(val):
    return val.__len__()
  
#調用函數
print(my_len('hello')) #5
print(my_len([1,2,3])) #3
print(my_len({'a':1,'b':2})) #2

#方式三
len('hello') #5
len([1,2,3]) #3
len({'a':1,'b':2}) #2

3 限制子類必須擁有父類方法名

多態性的本質在於不同的類中定義有相同的方法名,這樣我們就可以不考慮類而統一用一種方式去使用對象,可以通過在父類引入抽象類的概念來硬性限制子類必須有某些方法名

import abc

#指定 metaclass屬性將類設置為抽象類,抽象類本身只是用來約束子類的,不能被實例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod #該裝飾器限制子類義必須要定義一個名為talk方法
    def talk(self): #抽象類方法中無需實現具體的功能,也就是說只要定義函數方法名,無需函數體具體功能
        pass


class Cat(Animal): #但凡繼承 Animal 的子類都必須遵循 Animal 規定的標准
    def talk(self):
        pass


# cat = Animal() #報錯《Can't instantiate abstract class Animal with abstract methods talk》,抽象類不能被實例化
cat = Cat() ## 若子類中沒有一個名為talk的方法則會拋出異常TypeError,無法實例化

4 鴨子類型

依賴於繼承,只需要制造出外觀和行為相同對象,同樣也可以實現不考慮對象類型而使用對象,這正是Python崇尚的“鴨子類型”(duck typing):“如果看起來像、叫聲像而且走起路來像鴨子,那么它就是鴨子”。

比起繼承的方式,鴨子類型在某種程度上實現了程序的松耦合度,如下

#二者看起來都像文件(它們都有文件的方法),因而就可以當文件一樣去用,然而它們並沒有直接的關系
class Txt: #Txt類有兩個與文件類型同名的方法,即read和write
    def read(self):
        pass
    def write(self):
        pass

class Disk: #Disk類也有兩個與文件類型同名的方法:read和write
    def read(self):
        pass
    def write(self):
        pass

二 綁定方法與非綁定方法

綁定方法:特殊之處在於將調用者本身當做第一個參數傳入

綁定方法分為:綁定給對象的方法和綁定給類的方法

1 對象的綁定方法

調用者是對象,自動傳入的是對象本身

案例:

class Mysql:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

    def func(self):
        print('%s:%s' %(self.ip,self.port))

      
obj1=Mysql('1.1.1.1',3306)
print(obj1.__dict__) #{'ip': '1.1.1.1', 'port': 3306}
obj1.func()

#結果展示
'''
{'ip': '1.1.1.1', 'port': 3306}
1.1.1.1:3306
'''

2 類的綁定方法

調用者是類,自動傳入的是類本身

一般默認是對象的綁定方法,如果要指定一個方法綁定給類,需要給該方加上裝飾器 classmethod ,即將函數裝飾成綁定給類的方法

案例:

'''
settings.py

IP = '127.0.1'
PORT = '3306'
'''


import settings
class Mysql:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

    def func(self):
        print('%s:%s' %(self.ip,self.port))

    @classmethod # 將下面的函數裝飾成綁定給類的方法
    def from_conf(cls):
        print(cls) #<class '__main__.Mysql'>
        return cls(settings.IP, settings.PORT) #通過調用類的綁定方法,返回實例化得到的對象


obj2 = Mysql.from_conf()
print(obj2.__dict__) #{'ip': '127.0.0.1', 'port': 3306}

#結果展示
'''
<class '__main__.Mysql'>
{'ip': '127.0.0.1', 'port': 3306}
'''

3 非綁定方法

沒有綁定給任何人:調用者可是類,也可以是對象, 沒有自動傳參的效果,需要在指定方法上加裝飾器 staticmethod,即將函數裝飾城一個靜態方法

案例:

class Mysql:
    def __init__(self,ip,port):
        self.nid=self.create_id()
        self.ip=ip
        self.port=port

    @staticmethod # 將下述函數裝飾成一個靜態方法
    def create_id():
        import uuid
        return uuid.uuid4()

    @classmethod  #將下述函數裝飾城一個類的綁定方法
    def f1(cls):
        pass

    def f2(self):  #對象的綁定方法
        pass
obj1=Mysql('1.1.1.1',3306) 
print(Mysql.create_id) #<function Mysql.create_id at 0x10d8eb1e0>
print(obj1.create_id)  #<function Mysql.create_id at 0x10d8eb1e0>

print(Mysql.f1) #<bound method Mysql.f1 of <class '__main__.Mysql'>>
print(obj1.f2) #<bound method Mysql.f2 of <__main__.Mysql object at 0x10f7c0400>>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM