【Python面向對象】封裝機制的實現:類,及其語法特性

類,也許是從計算機的角度去結構這個世界的唯一方式。本文是一篇入門級別的文章,是有關於Python類的基礎語法特性。主要內容有:類的方法(即C/C++中的函數),類的屬性(即成員變量)。

寫在前面:
本文是一篇學習筆記、有一定的總結性的文章,可能只適合有相關基礎的人作爲複習和查漏補缺。也許不適合新手!

如果你是新手,它可能適合你!

如果你是新手,它可能適合你!

類的定義

以Student類爲例,在Python中,定義類是通過class關鍵字:

class Student(object):
	pass

解釋:

  • class後面緊接着是類名,即Student,
  • 類名通常是大寫開頭的單詞,緊接着是(object),表示該類是從哪個類繼承下來的,繼承的概念我們後面再講,
  • 通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。

類方法

類方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不同。

三種類的方法

  1. 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  2. 類方法:由調用; 至少一個cls參數;執行類方法時,自動將調用該方法的類複製給cls;
  3. 靜態方法:由調用;無默認參數;

三種類方法的調用:

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

    def ord_func(self):  # 定義普通方法,至少有一個self參數
        print('普通方法')

    @classmethod  # 定義類方法,至少有一個cls參數
    def class_func(cls):
        print('類方法')

    @staticmethod  # 定義靜態方法 ,無默認參數
    def static_func():
        print('靜態方法')

# 調用普通方法
f = Foo()
f.ord_func()

# 調用類方法
Foo.class_func()

# 調用靜態方法
Foo.static_func()

三種方法的區別:

  • 相同點:對於所有的方法而言,均屬於類(非對象)中,所以,在內存中也只保存一份。
  • 不同點:方法調用者不同、調用方法時自動傳入的參數不同。

特殊的內置方法

  1. __init__ 構造函數,在生成對象時調用
  2. __del__析構函數,釋放對象時使用
  3. __str__ 顯示該類的實例的信息,類似於Java中Object類的toString方法

爲了不喧賓奪主,這裏這是簡單介紹一下內置方法,更加詳細的內置方法,後文將更加詳細地介紹!

init內置方法

__init__()

  • 類似於Java中的帶參構造方法,特殊方法“init”前後分別有兩個下劃線!!!
  • 第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因爲self就指向創建的實例本身。
  • self類似於Java中的this指針;
class Student(object): # 定義類
    def __init__(self, name, score): # 定義構造方法
		self.name = name
		self.score = score

del內置方法

__del__():
理解del內置方法:

  1. 與 init() 方法對應的是__del__() 方法,__init__() 方法用於初始化 Python 對象,
  2. __del__() 則用於銷燬 Python 對象,
  3. 即在任何 Python 對象將要被系統回收之時,系統都會自動調用該對象的__del__() 方法。

當程序不再需要一個 Python 對象時,系統必須把該對象所佔用的內存空間釋放出來,這個過程被稱爲垃圾回收(GC,Garbage Collector),Python 會自動回收所有對象所佔用的內存空間,因此開發者無須關心對象垃圾回收的過程

**注意:**對一個變量執行 del 操作,該變量所引用的對象就會被回收,只有當對象的引用計數變成 0 時,該對象纔會被回收。因此,如果一個對象有多個變量引用它,那麼 del 其中一個變量是不會回收該對象的。

class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __del__ (self):  # 定義析構函數
        print('del刪除對象')

im = Item('鼠標', 29.8)  # 創建一個Item對象,將之賦給im變量
x = im   # 打印im所引用的Item對象

del im
print('--這是最後一句Python語句--')  # 只有執行最後一句,該類的對象不再被使用,才del

'''輸出:
--這是最後一句Python語句--
del刪除對象
'''

str內置方法

__str__():
類似於Java中的“toString”方法;用於輸出本類的信息;

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

    def __str__(self):
        return 'Student object (name: %s)' % self.name


print(Student('Michael'))  #  輸出:Student object (name: Michael)
# 如果不定義"__str__",輸出:<__main__.Student object at 0x109afb190>

類屬性

屬性的定義有兩種方式:

  1. 裝飾器,即:在方法上應用裝飾器
  2. 靜態字段,即:在類中定義值爲property對象的靜態字段

在哪裏定義類屬性:
1、直接在類中定義屬性

class UserInfo(object):
    name = '兩點水'

2、在構造函數中定義屬性

class UserInfo(object):
    def __init__(self, name):
        self.name = name

類變量的定義方式:

  1. xx公有變量
  2. _xx:單前置下劃線,受保護的屬性或方法(相當於protected),類對象和子類可以訪問,from somemodule import *禁止導入
  3. __xx:雙前置下劃線,私有化屬性或方法(相當於private),無法在外部直接訪問(名字重整所以訪問不到),訪問這種變量只能通過getter和setter函數來實現。
  4. __xx__:雙前後下劃線,系統定義名字(不要自己發明這樣的名字)
  5. xx_:單後置下劃線,用於避免與Python關鍵詞的衝突

靜態變量

有兩種方式:

  • 普通方式
  • property方式

普通靜態變量

class Province:
    country = '中國'  # 類的靜態變量,類似於Java中的static

    def __init__(self, name):
        self.name = name   # 普通(對象)的屬性


obj = Province('河北省')  # 直接訪問普通屬性
print(obj.name)

print(Province.country)  # 直接訪問靜態變量

• 靜態變量在內存中只保存一份
• 普通變量在每個對象中都要保存一份
應用場景: 通過類創建對象時,如果每個對象都具有相同的字段,那麼就使用靜態字段

property的靜態變量

快速理解property方法:

class Foo:
    def get_bar(self):
        return 'constant info'
    BAR = property(get_bar)  # 創建常量"BAR",把get_bar方法的返回值賦值給它;

obj = Foo()
reuslt = obj.BAR    # 自動調用get_bar方法,並獲取方法的返回值
print(reuslt)

property的構造方法中有個四個參數:

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.doc ,此參數是該屬性的描述信息
class Foo:
    def get_bar(self):
        return 'wupeiqi'
    def set_bar(self, value):  # *必須兩個參數
        return
        return 'set value' + value
    def del_bar(self):
        return 'wupeiqi'
    BAR = property(get_bar, set_bar, del_bar, 'description...') # 定義一個BAR常量

obj = Foo()

obj.BAR  # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "alex"  # 自動調用第二個參數中定義的方法:set_bar方法,並將“alex”當作參數傳入
del Foo.BAR  # 自動調用第三個參數中定義的方法:del_bar方法
obj.BAE.__doc__  # 自動獲取第四個參數中設置的值:description...

操作靜態字段

  • 由於靜態字段方式創建屬性具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除
class Goods(object):

    def __init__(self):
        self.original_price = 100  # 原價
        self.discount = 0.8  # 折扣

    def get_price(self):
        new_price = self.original_price * self.discount  # 實際價格 = 原價 * 折扣
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self, value):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '價格屬性描述...')


obj = Goods()
obj.PRICE         # 獲取商品價格
print('現在商品的價格是:',obj.PRICE)
obj.PRICE = 200   # 修改商品原價
print('修改後商品的價格是:',obj.PRICE)
#del obj.PRICE     # 刪除商品原價

裝飾器

如果你已經瞭解Python類中的方法,那麼屬性就非常簡單了,因爲Python中的屬性其實是普通方法的變種。即,通過“@property”裝飾器,將一個方法變成一個變量或常量。

裝飾器的類變量

class Goods:
    @property
    def price(self):
        return 'hahha'

obj = Goods()
result = obj.price  # 完全把price()方法,搞成了一個變量;如果result = obj.price()  錯誤!
print(result)  # 輸出:hahha
# print(obj.price())  # 報錯:TypeError: 'str' object is not callable

屬性的定義和調用要注意一下幾點:
• 定義時,在普通方法的基礎上添加 @property 裝飾器;
• 定義時,屬性僅有一個self參數
• 調用時,無需括號,即被@property裝飾的方法不再是一個方法,而是一個類屬性(類變量);

注意: 屬性存在意義是:訪問屬性時可以製造出和訪問字段(類變量)完全相同的假象
屬性由方法變種而來,如果Python中沒有屬性,方法完全可以代替其功能。

裝飾器與經典類、新式類

經典類(類名後的括號裏沒繼承“object”):
# ############### 定義 ###############    
class Goods:
    @property
    def price(self):
        return "wupeiqi"
    
# ############### 調用 ###############

obj = Goods()
result = obj.price  # 使得price方法變成一個變量,並獲取方法的返回值
print(result)

新式類(類名後的括號裏有“object”):具有三種@property裝飾器
# ############### 定義 ###############
# 新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法
class Goods(object):

    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 調用 ###############
obj = Goods()
obj.price          # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
obj.price = 123    # 自動執行 @price.setter 修飾的 price 方法,並將 123 賦值給方法的參數
del obj.price      # 自動執行 @price.deleter 修飾的 price 方法

核心點:

  • 經典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
  • 新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法

新式類的三個裝飾

class Goods(object):
    def __init__(self):
        self.original_price = 100  # 原價
        self.discount = 0.8   # 折扣

    @property
    def price(self):
        new_price = self.original_price * self.discount   # 實際價格 = 原價 * 折扣
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 獲取商品價格
print('原商品價格:',obj.price)  # 輸出:原商品價格: 80.0

obj.price = 200   # 修改商品原價
print('修改後商品價格:',obj.price)  # 輸出:修改後商品價格: 160.0

del obj.price     # 刪除商品原價

內置變量:name

在有些Python文件中,在最後一行常見這樣的代碼:

`if __name__ == '__main__':`

這到底是什麼意思呢?

  1. __name__是一個變量。前後加了雙下劃線是因爲這是系統定義的名字。普通變量不要使用此方式命名變量。

  2. Python有很多模塊,而這些模塊是可以獨立運行的!這點不像C++和C的頭文件。

  3. import的時候是要執行所import的模塊的。

  4. __name__就是標識模塊的名字的一個系統變量。這裏分兩種情況:假如當前模塊是主模塊(也就是調用其他模塊的模塊),那麼此模塊名字就是__main__,通過if判斷這樣就可以執行“__mian__:”後面的主函數內容;假如此模塊是被import的,則此模塊名字爲文件名字(不加後面的.py),通過if判斷這樣就會跳過“__mian__:”後面的內容。

通過上面方式,python就可以分清楚哪些是主函數,進入主函數執行;並且可以調用其他模塊的各個函數等等。

  1. 如果模塊是被導入,__name__的值爲模塊名字
  2. 如果模塊是被直接執行,__name__的值爲’__main__

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章