Python基礎學習(26)classmethod/staticmethod 裝飾器 部分內建魔術方法
Python基礎學習(26)classmethod/staticmethod 裝飾器 部分內建魔術方法
一、今日內容大綱
- classmethod staticmethod 裝飾器
- 部分內建魔術方法
二、classmethod staticmethod 裝飾器
-
classmethod 裝飾器:對裝飾的繫結方法會變成類方法
為了瞭解 classmethod 裝飾器到底有什麼作用,我們繼續使用之前舉過的售賣蘋果的例子:
class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount apple = Goods() print(apple.price) # 4.0
這時我們如果想修改折扣為 6 折,可以進行下列修改:
# 修改折扣 0.6 class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount def change_discount(self, new_discount): # self引數沒有利用 Goods.__discount = new_discount # 雖然成功修改了,但是好像有點兒不和邏輯 apple = Goods() apple.change_discount(0.6) apple2 = Goods() print(apple2.price) # 3.0
可以看到,雖然我們能夠成功實現修改購物商場的全場折扣,但是內建方法中的 self 引數是完全沒有利用的,且修改全場折扣要利用到個別物件的繫結方法,也就是說我們如果想要修改整個全場折扣,還要首先例項化一個物件,再依靠物件來修改折扣。這樣顯然是不符合正常邏輯的,所以我們使用 classmethod 裝飾器來將此方法定義為一個類方法:只可以被類呼叫(實際使用其實也可以被物件呼叫,但是一般不可以這麼使用):
# 定義了一個方法,預設傳self,但這個self沒被使用, # 這時我們使用classmethod裝飾器 class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount @classmethod def change_discount(cls, new_discount): # 將self換成類名傳入 cls.__discount = new_discount Goods.change_discount(0.6) # 可以直接使用方法名修改,無需先例項化 apple2 = Goods() print(apple2.price) # 3.0
所以一般什麼情況下,才使用 classmethod 裝飾器呢?
- 定義了一個方法,預設傳 self,但是這個 self 沒有被使用;
- 這個方法裡用到了當前的類名,或者你準備使用這個類的記憶體空間中的名字的時候;
我們來舉一個實際應用中的例子:定義一個類
Date
,內部有年、月、日等例項變數,Date.today()
可以返回一個儲存當天年月日的物件。import time class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day @classmethod def today(cls): obj = cls(time.localtime().tm_year, time.localtime().tm_mon, time.localtime().tm_mday) return obj time_obj = Date.today() print(time_obj.__dict__)
-
staticmethod 裝飾器:被裝飾的繫結方法會變成一個靜態方法
class User: pass @staticmethod # 本身是一個普通的函式,被挪到類的內部執行,那麼直接給這個函式添加了@staticmethod裝飾器就可以了 def login(): print('login') # 在函式的內部不會用到self變數,也不會用到cls類 User.login() # 除了前面要加上類名,其他使用方式與普通函式一致
-
小結
能定義到類的內容主要有:
- 靜態方法
- 繫結方法
- 類方法:利用 classmethod 裝飾器裝飾繫結方法實現
- 靜態方法:利用 staticmethod 裝飾器裝飾繫結方法實現
- 屬性/例項變數:可直接在
__init__
中定義或者利用 property 裝飾器裝飾繫結方法實現 - 靜態變數
三、部分內建魔術方法
-
總覽
魔術方法主要是指 Python 內建的
__func__
形式的方法,主要包括:__new__
__call__
__len__
__eq__
__str__
__repr__
__del__
__enter__
__exit__
-
__call__
方法:object_name()
呼叫此物件所屬類的__call__
方法。class A: def __call__(self, *args, **kwargs): print('----------') obj = A() print(callable(obj)) # True obj() # 呼叫__call__方法 ----------
-
__len__
方法:len(object_name)
呼叫此物件所屬類的__call__
方法。class Cls: def __init__(self, name): self.name = name self.students = [] def __len__(self): return len(self.students) py22 = Cls('py22') py22.students.append('duxiangxi') py22.students.append('taibai') py22.students.append('dazhuang') print(len(py22.students)) # 3 # 這兩者是等價的 print(len(py22)) # 3 print(py22.__len__()) # 3
-
__new__
方法:__new__
方法就是在例項化之前所完成的操作,例項化的過程會先執行__new__
方法,再執行__init__
方法,例項化過程返回的物件也就是__new__
返回的物件。# __new__ class A: def __new__(cls, *args, **kwargs): o = super().__new__(cls) # o = object.__new__(cls) # 因為父類是object也可以這麼寫 print('new', o) return o def __init__(self): print('init', self) A() # new <__main__.A object at 0x0000016947968B38> # init <__main__.A object at 0x0000016947968B38> # 例項化的時候 # 先建立一塊物件空間,有一個指標能指向類 -> 由__new__完成 # 呼叫__init__ # __new__是一個構造方法 # 為什麼要自己實現一個__new__呢? # 設計模式 --> 單例模式 # 一個類 從頭到尾 只會建立一次self的空間 class Baby: __instance = None def __new__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super().__new__(cls) return cls.__instance def __init__(self, cloth, pants): self.cloth = cloth self.pants = pants b1 = Baby('紅毛衣', '綠皮褲') b2 = Baby('白襯衫', '黑豹紋') print(b1) # <__main__.Baby object at 0x0000022322CFDB00> print(b2) # <__main__.Baby object at 0x0000022322CFDB00> print(b1.cloth) # 白襯衫 print(b2.cloth) # 白襯衫 # 利用模組的方式實現單例模式是最便捷的方法
-
__str__
和__repr__
方法:在列印一個物件或者len(object_name)
的時候,呼叫物件內部的__str__
方法,如果物件內部不存在__str__
方法則會呼叫備用的__repr__
方法。# 2.__str__ __repr__ class Course: def __init__(self, name, price, period): self.name = name self.price = price self.period = period def __str__(self): # 它只能返回str資料型別 return ','.join([self.name, str(self.price), self.period]) python = Course('python', 21800, '6 months') linux = Course('linux', 19800, '3 months') mysql = Course('mysql', 12800, '4 months') go = Course('python', 21800, '4 months') print(go) # python,21800,4 months lst = [python, linux, mysql, go] for course in lst: print(course) # for index, c in enumerate(lst, 1): # print(index, c.name, c.price, c.period) # num = int(input('>>>')) # course = lst[num-1] # print(f'you choose {course.name} {course.price}.') # 在列印一個物件的時候,呼叫__str__方法 # 在str一個物件的時候,呼叫__str__方法 # 當我們列印一個物件的時候, 用%s進行字串批結,或者str(物件) 總是呼叫這個物件的__str__方法 # 如果找不到__str__就呼叫__repr__方法 # __repr__不僅僅是__str__的替代品,還有自己的功能 # 用%r進行字串拼接 或者用repr(物件)的時候總是呼叫這個物件的__repr__方法