22. 物件導向之多型

hbutmeng發表於2024-08-16

1. 多型

1.1 概念

多型指的是一類事物有多種形態

比如動物有多種形態:人、猴、鴨

1.2 程式碼示例

from abc import ABC, abstractmethod

# 對於程式來說,定義一個基類可以有多個子類
class Animal(ABC):
    @abstractmethod
    def run(self):
        pass

    @abstractmethod
    def speak(self, name):
        print(f'{name}正在說話')

# 第一種形態:人
class Person(Animal):
    def run(self):
        print('可以潤')

    def speak(self, name):
        print(f'{name}正在說話')

# 第二種形態:猴
class Monkey(Animal):
    def run(self):
        print('猴哥也可以潤')

# 第三種形態:鴨子
class Duck(Animal):
    def run(self):
        print('鴨子也可以潤')

1.3 多型性

動態繫結:在程式執行的過程中,根據物件的型別,動態的將方法進行繫結

動態多樣性:動態繫結在繼承背景下的特性就叫動態多型性

靜態多樣性:如任何型別都可以用運算子加號+ 進行運算

from abc import ABC, abstractmethod

# 同一類事物:動物
class Animal(ABC):
    @abstractmethod
    def run(self):
        pass

# 第一種形態:人
class Person(Animal):
    def run(self):
        print('可以潤')

# 第二種形態:猴
class Monkey(Animal):
    def run(self):
        print('猴哥也可以潤')

# 第三種形態:鴨子
class Duck(Animal):
    def run(self):
        print('鴨子也可以潤')

person1 = Person()
monkey1 = Monkey()
duck1 = Duck()

# person1、monkey1、duck1都是動物,只要是動物肯定有run方法
# 於是可以不用考慮三個物件的具體是宣告型別,而直接使用
person1.run()
monkey1.run()
duck1.run()

# 更進一步,可以定義一個統一的介面來使用
def func(obj):
    obj.run()

python中一切皆物件,本身就支援多型性

# 在不考慮物件型別的情況下統計物件的長度
a.__len__()
b.__len__()
c.__len__()

# python內建了一個統一的介面
len(a)
len(b)
len(c)

2. 鴨子型別

鴨子型別(duck typing)是一種程式設計風格,不是一個真實存在的約束關係,是一種普遍的規範

看起來像、聲音像、走起路來像鴨子,那麼它就是鴨子

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

    def write(self):
        pass


class Video:  # Video類也有兩個與檔案型別同名的方法,即read和write
    def read(self):
        pass

    def write(self):
        pass

3. 繫結方法和非繫結方法

3.1 概念

繫結方法(動態方法):

繫結給誰,誰來呼叫就自動將它本身當作第一個引數傳入

繫結給類的方法、繫結給物件的方法

非繫結方法(靜態方法):

既不繫結給類也不繫結給物件

一句話概括:方法即函式,可以理解為繫結函式、非繫結函式

3.2 繫結給物件的方法

概述:

  繫結給物件的方法就是在類內部直接定義的方法------即在類內部寫的函式,寫的時候自動帶上self引數

  特點就是會自動補全self引數

  物件呼叫繫結給物件的方法的時候不需要傳入self引數,類呼叫物件的繫結方法的時候需要傳入self引數(物件名)

class Student:

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

    def run(self):  # 繫結給物件的方法就是在類內部定義的方法,自動補全self物件
        print(f'這是run方法,{self.name}可以潤')


stu1 = Student(name='lavigne')  # 例項化得到的物件

# (1)物件呼叫物件的繫結方法
#    不需要傳遞self引數,在物件呼叫物件的繫結方法的時候會自動將物件作為self引數傳進去
stu1.run()  # 這是run方法,lavigne可以潤

# (2)類呼叫物件的繫結方法
#    必須主動給self傳入一個物件
stu2 = Student(name='avril')
# Student.run()  # 報錯  TypeError: Student.run() missing 1 required positional argument: 'self'
Student.run(stu1)  # 這是run方法,lavigne可以潤
Student.run(stu2)  # 這是run方法,avril可以潤

3.3 繫結給類的方法  classmethod

類在使用時會將類本身當作引數傳給類方法的第一個引數(即便是物件來呼叫也會將類當作第一個引數傳入)

classmethod把類中的函式定義成類方法

class Student:
    def __init__(self, name):
        self.name = name

    @classmethod
    def read(cls):  # 繫結給類的方法,必須用classmethod裝飾器,並且在定義函式的時候預設補全引數cls
        print(f'當前是繫結給類的read方法:{cls}')


# (1)物件呼叫繫結給類的方法
#    不需要傳入cls引數
#    會自動識別例項化當前物件的類,直接將類傳入
stu1 = Student(name='lavigne')
stu1.read()  # 當前是繫結給類的read方法:<class '__main__.Student'>

# (2)類呼叫繫結給類的方法
#    不需要傳入cls引數
#    預設將當前類作為cls的預設引數傳入
Student.read()  # 當前是繫結給類的read方法:<class '__main__.Student'>

總結:

  定義繫結給類的方法的時候,必須用裝飾器@classmethod;類似於繫結給物件的方法函式,會自動補全引數cls

  物件呼叫繫結給類的方法,不需要傳入cls引數

  類呼叫繫結給類的方法,不需要傳入cls引數

3.4 非繫結方法  staticmethod

在類內部用staticmethod裝飾的函式即非繫結方法,就是普通函式

staticmethod既不繫結給類,也不繫結給物件,在定義函式的時候和普通函式一樣不會補全任何預設引數

相關文章