博客 / 詳情

返回

Python 面向對象

0x01 面向對象概念

類和對象都是面向對象中的重要概念。面向對象是一種編程思想,即按照真實世界的思維方式構建軟件
系統。
例如,在真實世界的校園裏有學生和老師,學生有學號、姓名、所在班級等屬性(數據),還有學習、提問、吃飯和走路等動作(方法)。如果我們要開發一個校園管理系統,那麼在構建軟件系統時,也會有學生和老師等“類”,張同學、李同學是學生類的個體,被稱為“對象”,"對象"也被稱為"實例"。

0x02 定義類

Python中的數據類型都是類,我們可以自定義類,即創建一種新的數據類型。

class 類名[(父類)]:   #父類可以省略聲明,表示直接繼承object類
	類體   #縮進,在Python中推薦採用4個半角空格
class Car(object):
	#類體
	pass   #pass主要用於佔位

Car 類繼承了 object 類,object 類是所有類的根類,在 Python 中任何一個類(除object外)都直接或間接地繼承了 object,直接繼承 object 時(object)部分的代碼可以省略。

0x03 創建對象

類相當於一個模板,依據這樣的模板來創建對象,就是類的實例化,所以對象也被稱為"實例"。
image

class Car(object):
	#類體
	pass   #pass主要用於佔位
car=car()

0x04 類成員

image
成員變量也成為數據成員,保存了類或對象的數據。例如,學生的姓名和學號。
構造方法事一種特殊的函數,用於初始化類的成員變量。
成員方法是在類中定義的函數。
屬性是對類進行封裝而提供的特殊方法。

實例變量

實例變量就是對象個體特有的"數據"。

class Dog:
    def __init__(self,name,age):   #__init__()方法是構造方法,用來初始化實例變量
        self.name=name   #創建和初始化實例變量name
        self.age=age   #創建和初始化實例變量age
d=Dog('球球',2)   #創建對象
print('我們家狗狗叫{0},{1}歲了'.format(d.name,d.age))   #對實例變量通過"對象.實例變量"形式訪問
輸出結果為:
我們家狗狗叫球球,2歲了

類中的 self 表示當前對象,構造方法中的 self 參數説明這個方法屬於實例,self.age 中的 self 表示 age 屬於實例,即實例成員變量。

構造方法

類中的__init__()方法是一個非常特殊的方法,用來創建和初始化實例變量,這種方法就是"構造方法"。在定義__init__()方法時,他的第1個參數應該是self,之後的參數用來初始化實例變量。調用構造方法時不需要傳入self參數。

class Dog:
    def __init__(self, name,age,sex='雌性'):   #第1個參數必須是self,帶有默認值的構造方法能夠提供多個不同版本的構造方法
        self.name=name   #創建和初始化實例變量name
        self.age=age   #創建和初始化實例變量age
        self.sex=sex   #創建和初始化實例變量sex
d1=Dog('球球',2)   #創建對象調用構造方法,省略默認值
d2=Dog('哈哈',1,'雄性')
d3=Dog(name='拖布',sex='雄性',age=3)   #使用關鍵字參數調用構造方法
print('{0}:{1}歲{2}'.format(d1.name,d1.age,d1.sex))
print('{0}:{1}歲{2}'.format(d2.name,d2.age,d2.sex))
print('{0}:{1}歲{2}'.format(d3.name,d3.age,d3.sex))

實例方法

實例方法與實例變量一樣,都是某個實例(或對象)個體特有的方法。定義實例方法時,它的第1個參數也應該是self,這會將當前實例與該方法綁定起來,這也説明該方法屬於實例。在調用方法時不需要傳入self,類似於構造方法。

class Dog:
    def __init__(self, name,age,sex='雌性'):
        self.name=name
        self.age=age
        self.sex=sex
    def run(self):   #定義實例方法,只有一個self參數
        print("{}在跑...".format(self.name))
    def speak(self,sound):   #定義實例方法,第1個參數是self,第2個參數是sound
        print('{}在叫,"{}"'.format(self.name,sound))
dog=Dog('球球',2)   #創建對象調用構造方法,省略默認值
dog.run()   #在調用時採用"對象.實例方法"形式,不需要傳遞參數
dog.speak('汪汪汪')   #需要傳遞一個參數sound
輸出結果為:
球球在跑...
球球在叫,"汪汪汪"

類變量

類變量是屬於類的變量,不屬於單個對象。
例如,有一個Account(銀行賬户)類,它有三個成員變量:amount(賬户金額)、interest_rate (利率)和owner(賬户名)。amount和owner對於每一個賬户都是不同的,而interest_rate對於所有賬户都是相同的。amount和owners是實例變量,interest_rate是所有賬户實例共享的變量,它屬於類,被稱為"類變量"。

class Account:
    interest_rate=0.0568
    def __init__(self,owner,amount):
        self.owner=owner
        self.amount=amount
account=Account('Tony',800000.0)
print('賬户名:{0}'.format(account.owner))   #對實例變量通過"對象.實例變量"形式訪問
print('賬户金額:{0}'.format(account.amount))
print('利率:{0}'.format(Account.interest_rate))   #對象變量通過"類名.類變量"形式訪問
輸出結果為:
賬户名:Tony
賬户金額:800000.0
利率:0.0568

類方法

類方法與類變量類似,屬於類,不屬於個體實例。在定義類方法時,它的第1個參數不是self,而是類本身。

class Account:
    interest_rate=0.0568
    def __init__(self,owner,amount):
        self.owner=owner
        self.amount=amount
    @classmethod   #定義類方法需要的裝飾器,以@開頭修飾函數、方法和類,用來約束他們
    def interest_by(cls,amt):   #cls代表類自身,即Account類
        return cls.interest_rate*amt   #cls可以直接用Account替換
interest=Account.interest_by(12000.0)   #對類方法可以通過"類名.類方法"形式訪問
print('計算利息:{0:.4f}'.format(interest))
輸出結果為:
計算利息:681.6000

0x05 封裝

封裝性是面向對象重要的基本特性之一。封裝隱藏了對象的內部細節,只保留有限的對外接口,外部調
用者不用關心對象的內部細節,使得操作對象變得簡單。例如,一台計算機內部極其複雜,有主板、CPU、硬盤和內存等,而一般人不需要了解它的內部細節。計算機制造商用機箱把計算機封裝起來,對外提供了一些接口,如鼠標、鍵盤和顯示器等,使用計算機就變得非常簡單。

  • 雙下劃線表示私有屬性
  • 單下劃線表示受保護的沒用下劃線就是公共的
  • 沒用下劃線就是公共的

私有變量

為了防止外部調用者隨意存取類的內部數據(成員變量),內部數據(成員變量)會被封裝為"私有變
量"。外部調用者只能通過方法調用私有變量。在默認情況下,Python中的變量是公有的,可以在類的外部訪問它們。如果想讓它們成為私有變量,則在變量前加上雙下畫線(__)即可。

class Account:
    __interest_rate=0.0568
    def __init__(self,owner,amount):
        self.owner=owner
        self.__amount=amount
    def desc(self):   #在類的內部可以訪問私有變量
        print("{0}金額:{1}利率:{2}".format(self.owner,self.__amount,Account.__interest_rate))
account=Account('Tony',800000.0)
account.desc()
print('賬户名:{0}'.format(account.owner))
print('賬户金額:{0}'.format(account.__amount))   #錯誤發生,在類的外部不可以訪問私有變量
print('利率:{0}'.format(Account.__interest_rate))   #錯誤發生
#輸出結果為:
print('賬户金額:{0}'.format(account.__amount))
AttributeError: 'Account' object has no attribute '__amount'
Tony金額:800000.0利率:0.0568
賬户名:Tony

私有方法

私有方法與私有變量的封裝是類似的,在方法前加上雙下畫線(__)就是私有方法了。

class Account:
    __interest_rate=0.0568
    def __init__(self,owner,amount):
        self.owner=owner
        self.__amount=amount
    def __get_info(self):   #定義私有方法
        print("{0}金額:{1}利率:{2}".format(self.owner,self.__amount,Account.__interest_rate))
    def desc(self):
        print(self.__get_info())   #在類的內部可以調用私有方法
account=Account('Tony',800000.0)
account.desc()
account.__get_info()   #在類的外部調用私有方法,發生錯誤
#輸出結果為:
AttributeError: 'Account' object has no attribute '__get_info'

屬性的使用

為了實現對象的封裝,在一個類中不應該有公有的成員變量,這些成員變量應該被設計為私有的,然後通過公有的set(賦值)和get(取值)方法訪問。

class Dog:
	#構造方法
    def __init__(self,name,age,sex='雌性'):
        self.name=name   #創建和初始化實例變量name
        self.__age=age   #創建和初始化私有實例變量__age
	#實例方法
    def run(self):
        print("{}在跑...".format(self.name))
	#get方法
    def get_age(self):   #定義get()方法,返回私有實例變量__age
        return self.__age
	#set方法
    def set_age(self,age):   #定義set()方法,通過age參數更新私有實例變量__age
        self.__age=age
dog=Dog('球球',2)
print('狗狗年齡:{}'.format(dog.get_age()))   #通過get()方法取值
dog.set_age(3)   #通過set()方法賦值
print('修改後狗狗年齡:{}'.format(dog.get_age()))   #通過get()方法取值

上面的示例中,當外部調用者通過兩個公有方法訪問被封裝的私有成員變量時,會比較麻煩。我們可以在類中定義屬性,屬性可以替代get()和set()這兩個公有方法,在調用時比較簡單。

class Dog:
    def __init__(self,name,age,sex='雌性'):
        self.name=name
        self.__age=age   #私有變量__age,對應的屬性名應該去除前面雙下劃線之後的名稱,即age
    def run(self):
        print("{}在跑...".format(self.name))
    @property
    def age(self):   #定義age屬性的get()方法,使用@property裝飾器進行修飾,方法名就是屬性名即age
        return self.__age
    @age.setter
    def age(self,age):   #定義age屬性的set()方法,使用@age.setter裝飾器進行修飾,age是屬性名
        self.__age=age
dog=Dog('球球',2)
print('狗狗年齡:{}'.format(dog.age))   #可以通過屬性取值,訪問形式為"實例.屬性"
dog.age=3   #可以通過屬性賦值,訪問形式為"實例.屬性"
print('修改後狗狗年齡:{}'.format(dog.age))   #可以通過屬性取值,訪問形式為"實例.屬性"

屬性本質上就是兩個方法,在方法前加上裝飾器使得方法成為屬性。屬性使用起來類似於公有變量,可以在賦值符(=)左邊或右邊,左邊被賦值,右邊取值。

0x06 繼承

繼承性也是面向對象重要的基本特性之一。在現實世界中繼承關係無處不在。例如貓與動物之間的關係:貓是一種特殊動物,具有動物的全部特徵和行為,即數據和操作。在面向對象中動物是一般類,被稱為"父類";貓是特殊類,被稱為"子類"。特殊類擁有一般類的全部數據和操作,可稱之為子類繼承父類語法很簡單,定義類時在類的後面使用一對小括號指定它的父類就可以了。

class Animal:   #定義父類動物(Animal)
    def __init__(self,name):
        self.name=name
    def show_info(self):
        return "動物的名字:{0}".format(self.name)
    def move(self):
        print("動一動...")
class Cat(Animal):   #定義子類貓(Cat)
    def __init__(self,name,age):
        super().__init__(name)   #調用父類構造方法,初始化父類成員變量
        self.age=age
cat=Cat('Tom',2)
cat.move()
print(cat.show_info())

子類繼承父類時只有那些公有的成員變量和方法才可以被繼承。

多繼承

在Python中,當子類繼承多個父類時,如果在多個父類中有相同的成員方法或成員變量,則子類優先繼承左邊父類中的成員方法或成員變量,從左到右繼承級別從高到低。

class Horse:
    def __init__(self,name):
        self.name=name
    def show_info(self):
        return "馬的名字:{0}".format(self.name)
    def run(self):
        print("馬跑...")
class Donkey:
    def __init__(self,name):
        self.name=name
    def show_info(self):
        return "驢的名字:{0}".format(self.name)
    def run(self):
        print("驢跑...")
    def roll(self):
        print("驢打滾...")
class Mule(Horse,Donkey):
    def __init__(self,name,age):
        super().__init__(name)
        self.age=age
m=Mule('騾子',1)
m.run()   #繼承父類Horse方法
m.roll()   #繼承父類Donkey方法
print(m.show_info())   #繼承父類Horse方法

方法重寫: 如果子類方法名和父類方法名相同,子類方法會覆蓋掉父類的方法。

0x07 多態性

多態性也是面向對象重要的基本特性之一。“多態”指對象可以表現出多種形態。同其他語言差不多,父類表示子類,多用於傳遞參數。

class Animal:
    def speak(self):
        print('動物叫,但不知道是哪種動物叫!')
class Dog(Animal):
    def speak(self):
        print('小狗:旺旺叫...')
class Cat(Animal):
    def speak(self):
        print('小貓:喵喵叫...')
class Car:
    def speak(self):
        print('小汽車: 滴滴叫')
def start(obj):
    obj.speak()
start(Dog())
start(Cat())
start(Car())
輸出結果為:
小狗:旺旺叫...
小貓:喵喵叫...
小汽車: 滴滴叫
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.