(為了方便和美觀,省略了各內置方法前后的__雙下划線)
1、new、init
__new__
方法是真正的類構造方法,用於產生實例化對象(空屬性)。重寫__new__
方法可以控制對象的產生過程。
__init__
方法是初始化方法,負責對實例化對象進行屬性值初始化,此方法必須返回None,__new__
方法必須返回一個對象。重寫__init__
方法可以控制對象的初始化過程。
# 使用new來處理單例模式
class Student:
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def sleep(self):
print('sleeping...')
stu1 = Student()
stu2 = Student()
print(id(stu1), id(stu2)) # 兩者輸出相同
print(stu1 is stu2) # True
個人感覺,__new__
一般很少用於普通的業務場景,更多的用於元類之中,因為可以更底層的處理對象的產生過程。而__init__
的使用場景更多。
2、str、repr
兩者的目的都是為了顯式的顯示對象的一些必要信息,方便查看和調試。__str__
被print
默認調用,__repr__
被控制台輸出時默認調用。即,使用__str__
控制用戶展示,使用__repr__
控制調試展示。
# 默認所有類繼承object類,object類應該有一個默認的str和repr方法,打印的是對象的來源以及對應的內存地址
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
stu = Student('zlw', 26)
print(stu) # <__main__.Student object at 0x0000016ED4BABA90>
# 自定義str來控制print的顯示內容,str函數必須return一個字符串對象
# 使用repr = str來偷懶控制台和print的顯示一致
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.__class__}, {self.name}, {self.age}'
__repr__ = __str__
stu = Student('zlw', 26)
print(stu) # <class '__main__.Student'>, zlw, 26
3、call
__call__
方法提供給對象可以被執行的能力,就像函數那樣,而本質上,函數就是對象,函數就是一個擁有__call__
方法的對象。擁有__call__
方法的對象,使用callable
可以得到True
的結果,可以使用()
執行,執行時,可以傳入參數,也可以返回值。所以我們可以使用__call__
方法來實現實例化對象作為裝飾器:
# 檢查一個函數的輸入參數個數, 如果調用此函數時提供的參數個數不符合預定義,則無法調用。
# 單純函數版本裝飾器
def args_num_require(require_num):
def outer(func):
def inner(*args, **kw):
if len(args) != require_num:
print('函數參數個數不符合預定義,無法執行函數')
return None
return func(*args, **kw)
return inner
return outer
@args_num_require(2)
def show(*args):
print('show函數成功執行!')
show(1) # 函數參數個數不符合預定義,無法執行函數
show(1,2) # show函數成功執行!
show(1,2,3) # 函數參數個數不符合預定義,無法執行函數
# 檢查一個函數的輸入參數個數,
# 如果調用此函數時提供的參數個數不符合預定義,則無法調用。
# 實例對象版本裝飾器
class Checker:
def __init__(self, require_num):
self.require_num = require_num
def __call__(self, func):
self.func = func
def inner(*args, **kw):
if len(args) != self.require_num:
print('函數參數個數不符合預定義,無法執行函數')
return None
return self.func(*args, **kw)
return inner
@Checker(2)
def show(*args):
print('show函數成功執行!')
show(1) # 函數參數個數不符合預定義,無法執行函數
show(1,2) # show函數成功執行!
show(1,2,3) # 函數參數個數不符合預定義,無法執行函數
4、del
__del__
用於當對象的引用計數為0時自動調用。
__del__
一般出現在兩個地方:1、手工使用del減少對象引用計數至0,被垃圾回收處理時調用。2、程序結束時調用。
__del__
一般用於需要聲明在對象被刪除前需要處理的資源回收操作
# 手工調用del 可以將對象引用計數減一,如果減到0,將會觸發垃圾回收
class Student:
def __del__(self):
print('調用對象的del方法,此方法將會回收此對象內存地址')
stu = Student() # 調用對象的__del__方法回收此對象內存地址
del stu
print('下面還有程序其他代碼')
class Student:
def __del__(self):
print('調用對象的del方法,此方法將會回收此對象內存地址')
stu = Student() # 程序直接結束,也會調用對象的__del__方法回收地址
5、iter、next
這2個方法用於將一個對象模擬成序列。內置類型如列表、元組都可以被迭代,文件對象也可以被迭代獲取每一行內容。重寫這兩個方法就可以實現自定義的迭代對象。
# 定義一個指定范圍的自然數類,並可以提供迭代
class Num:
def __init__(self, max_num):
self.max_num = max_num
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count < self.max_num:
self.count += 1
return self.count
else:
raise StopIteration('已經到達臨界')
num = Num(10)
for i in num:
print(i) # 循環打印1---10
6、getitem、setitem、delitem
重寫此系列方法可以模擬對象成列表或者是字典,即可以使用key-value
的類型。
class StudentManager:
li = []
dic = {}
def add(self, obj):
self.li.append(obj)
self.dic[obj.name] = obj
def __getitem__(self, item):
if isinstance(item, int):
# 通過下標得到對象
return self.li[item]
elif isinstance(item, slice):
# 通過切片得到一串對象
start = item.start
stop = item.stop
return [student for student in self.li[start:stop]]
elif isinstance(item, str):
# 通過名字得到對象
return self.dic.get(item, None)
else:
# 給定的key類型錯誤
raise TypeError('你輸入的key類型錯誤!')
class Student:
manager = StudentManager()
def __init__(self, name):
self.name = name
self.manager.add(self)
def __str__(self):
return f'學生: {self.name}'
__repr__ = __str__
stu1 = Student('小明')
stu2 = Student('大白')
stu3 = Student('小紅')
stu4 = Student('胖虎')
# 當做列表使用
print(Student.manager[0]) # 學生: 小明
print(Student.manager[-1]) # 學生: 胖虎
print(Student.manager[1:3]) # [學生: 大白, 學生: 小紅]
# 當做字典使用
print(Student.manager['胖虎']) # 學生: 胖虎
7、getattr、setattr、delattr
當使用obj.x = y
的時候觸發對象的setattr
方法,當del obj.x
的時候觸發對象的delattr
方法。
當嘗試訪問對象的一個不存在的屬性時 obj.noexist
會觸發getattr
方法,getattr
方法是屬性查找中優先級最低的。
可以重寫這3個方法來控制對象屬性的訪問、設置和刪除。
特別注意:如果定義了getattr,而沒有任何代碼(即只有pass),則所有不存在的屬性值都是None而不會報錯,可以使用super().getattr()方法來處理
class Student:
def __getattr__(self, item):
print('訪問一個不存在的屬性時候觸發')
return '不存在'
def __setattr__(self, key, value):
print('設置一個屬性值的時候觸發')
# self.key = value # 這樣會無限循環
self.__dict__[key] = value
def __delattr__(self, item):
print('刪除一個屬性的時候觸發')
if self.__dict__.get(item, None):
del self.__dict__[item]
stu = Student()
stu.name = 'zlw' # 設置一個屬性值的時候觸發
print(stu.noexit) # 訪問一個不存在的屬性時候觸發 , 返回'不存在'
del stu.name # 刪除一個屬性的時候觸發
8、getatrribute
這是一個屬性訪問截斷器,即,在你訪問屬性時,這個方法會把你的訪問行為截斷,並優先執行此方法中的代碼,此方法應該是屬性查找順序中優先級最高的。
屬性查找順序:
實例的getattribute-->實例對象字典-->實例所在類字典-->實例所在類的父類(MRO順序)字典-->實例所在類的getattr-->報錯
class People:
a = 200
class Student(People):
a = 100
def __init__(self, a):
self.a = a
def __getattr__(self, item):
print('沒有找到:', item)
def __getattribute__(self, item):
print('屬性訪問截斷器')
if item == 'a':
return 1
return super().__getattribute__(item)
stu = Student(1)
print(stu.a) # 1
9、enter、exit
這兩個方法的重寫可以讓我們對一個對象使用with
方法來處理工作前的准備,以及工作之后的清掃行為。
class MySQL:
def connect(self):
print('啟動數據庫連接,申請系統資源')
def execute(self):
print('執行sql命令,操作數據')
def finish(self):
print('數據庫連接關閉,清理系統資源')
def __enter__(self): # with的時候觸發,並賦給as變量
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb): # 離開with語句塊時觸發
self.finish()
with MySQL() as mysql:
mysql.execute()
# 結果:
# 啟動數據庫連接,申請系統資源
# 執行sql命令,操作數據
# 數據庫連接關閉,清理系統資源