一、isinstance(obj,cls)和issubclass(sub,super)
1. isinstance(obj,cls): 檢查是否obj是否是類 cls 的對象
class Player:passp = Player()print(isinstance(p, Player))>>>
Ture
?
2.?issubclass(sub, super): 檢查sub類是否是 super 類的派生類
class Player:passclass Basketball(Player):passprint(issubclass(Basketball, Player))>>>
Ture
?
二、__getattribute__
1. 回顧__getattr__方法
class Player:x = 23y = 6def __init__(self):passdef __getattr__(self, item):print('不存在%s屬性' % item)p = Player()
p.x
p.xy>>>
不存在xy屬性
?
2. __getattribute__方法演示
class Player:x = 23y = 6def __init__(self):passdef __getattribute__(self, item):print('存不存在該屬性都會觸發__getattribute__方法')p = Player()
p.x
p.xy>>>
存不存在該屬性都會觸發__getattribute__方法
?
3. __getattr__與__getattribute__同時存在時
class Foo:def __init__(self, x):self.x = xdef __getattr__(self, item):print('執行的是__getattr__')# return self.__dict__[item]def __getattribute__(self, item):print('不管是否存在,__getattribute都會執行')raise AttributeError('拋出一個錯誤')f1=Foo(10)
f1.x
f1.xxxxxx>>>
不管是否存在,__getattribute都會執行#當__getattribute__與__getattr__同時存在,只會執行__getattrbute__, 若__getattribute__在執行過程中拋出異常AttributeError, 則先執行__getattribute__,然后執行__getattr__
?
三、__setitem__, __getitem, __delitem__
1.?__setitem__, __getitem__, __delitem__都是 obj[‘屬性’]的方式去操作屬性時觸發的方法
?__getitem__:obj['屬性'] 時觸發
__setitem__:obj['屬性']=屬性的值 時觸發
__delitem__:del obj['屬性'] 時觸發
2.與__getattr__, __setattr__, __delattr__的同和異
同:賦值的底層原理都一樣
f1.name = 'lebron' #做的就是往屬性字典里添加或覆蓋值
原理:----->__setattr__----->f1.__dict__['name'] = 'lebron'f1['name'] = 'lebron' 原理:----->__setitem__----->f1.__dict__['name'] = 'lebron'#兩個方法的底層都是一樣的,只不過通過__setattr__,__setitem__中轉了一下
異:
__getattr__,__setattr__,__delattr__
obj點的方式去操作屬性時觸發的方法
__getitem__,__setitem_,__delitem__
obj[‘屬性’]的方式去操作屬性時觸發的方法
__getattr__:obj.屬性不存在時觸發
__setattr__:obj.屬性=屬性的值 時觸發
__delattr__:del obj.屬性 時觸發
__getitem__:obj['屬性'] 時觸發
__setitem__:obj['屬性']=屬性的值 時觸發
__delitem__:del obj['屬性'] 時觸發
class Foo:def __init__(self,name):self.name=namedef __getitem__(self, item):print(self.__dict__[item])def __setitem__(self, key, value):self.__dict__[key]=valuedef __delitem__(self, key):print('del obj[key]時,我執行')self.__dict__.pop(key)def __delattr__(self, item):print('del obj.key時,我執行')self.__dict__.pop(item)f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
>>>
? del obj.key時,我執行
? del obj[key]時,我執行
? {'name': 'alex'}
?
四、__str__, __repr__
__str__: 控制返回值,并且返回值必須是str類型,否則報錯
__repr__:控制返回值并且返回值必須是str類型,否則報錯
__repr__是__str__的替代品,如果__str__存在,直接按__str__的返回值,返回信息,不會執行__repr__,如果不存在,會執行__repr__,并得到__repr__的返回值
li = list('12345')
print(type(li)) def foo():pass
print(type(foo)) >>>
<class 'list'>
<class 'function'>#調用了類內部的__str__方法,從而打印出<class 'list'>, <class 'function'>
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef __repr__(self):return '名字是%s,年齡是%s' %(self.name, self.age)def __str__(self):return 'name is %s, age is %s' %(self.name, self.age)
p1 = Foo('chen', '23')
print(p1)>>>
name is chen, age is 23#__str__和__repr__都存在時,只會執行__str__方法
?
五、自定制格式化字符串__format__
format_dic = {'ymd':'{0.year}{0.mon}{0.day}','y:m:d':'{0.year}:{0.mon}:{0.day}','y-m-d':'{0.year}-{0.mon}-{0.day}'
}
class Date:def __init__(self, year, mon, day):self.year = yearself.mon = monself.day = daydef __format__(self, format_spec):if not format_spec or format_spec not in format_dic: #用戶輸入的格式不存在時,使用默認的格式format_spec = 'y:m:d'return format_dic[format_spec].format(self)
d = Date('2019','04','01')
print(d.__format__('y-m-d'))
print(d.__format__('y:m:d'))
print(d.__format__('ymd'))
print(d.__format__('asdf'))>>>
2019-04-01
2019:04:01
20190401
2019:04:01
?
六、__slots__
1.__slots__是什么:是一個類變量,變量值可以是列表,元祖,或者可迭代對象,也可以是一個字符串(意味著所有實例只有一個數據屬性)
2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每個實例的是獨立的)
3.為何使用__slots__: 字典會占用大量內存,如果你有一個屬性很少的類,但是有很多實例,為了節省內存可以使用__slots__取代實例的
__dict__
當你定義__slots__后,__slots__就會為實例使用一種更加緊湊的內部表示。實例通過一個很小的固定大小的數組來構建,而不是為每個實
例定義一個字典。
4.注意事項:__slots__的很多特性都依賴于普通的基于字典的實現。另外, 定義了__slots__后的類不再支持一些普通類特性了,比如多
繼承。大多數情況下,你應該只在那些經常別使用的用作數據結構的類上定義
關于__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。盡管使用__slots__可以達到這樣的目的,但是
這個并不是它的初衷。 更多的是用來作為一個內存優化工具。
class Foo:__slots__ = ['name', 'age'] #__dict__ = {'name': None, 'age': None}
p = Foo()
p.name = 'chen'
p.age = '18'
#print(p.__dict__) 報錯, __slots__方法下,實例化對象不存在屬性字典
#p.height = '1.8' 報錯,__slots__方法下,實例化對象只能設置指定的屬性
?
七、__doc__
類的描述信息
class Foo: '我是描述信息' pass
print(Foo.__doc__)>>>'我是描述信息'
該屬性無法被繼承
class Foo:'我是描述信息'passclass Bar(Foo):pass
print(Bar.__doc__) #該屬性無法繼承給子類>>>
None
?
八、?__module__和__class__
__module__ 表示當前操作的對象在那個模塊
__class__ ? ? 表示當前操作的對象的類是什么
from test import Playerp = Player('lebron_james', 34, '2.03m')print(p.__module__)
print(p.__class__)>>>
test #輸出模塊名
<class 'test.Player'> #輸出對象的類名
?
九、__del__
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:如果產生的對象僅僅只是python程序級別的(用戶級),那么無需定義__del__, 如果產生的對象的同時還會向操作系統發起系統調用,即一個對象有用戶級與內核級兩種資源,比如(打開一個文件,創建一個數據庫鏈接),則必須在清除對象的同時回收系統資源,這就用到了__del__
1. 簡單示范
class Foo:def __del__(self):print('執行我啦')f1=Foo()
del f1
print('------->')>>>
執行我啦 #刪除f1對象,觸發__del__方法
------->class Foo:def __del__(self):print('執行我啦')f1=Foo()
print('------->')>>>
------->
執行我啦 #程序執行完畢,釋放f1對象內存,觸發__del__方法
?
2. 典型的應用場景:
創建數據庫類,用該類實例化出數據庫鏈接對象,對象本身是存放于用戶空間內存中,而鏈接則是由操作系統管理的,存放于內核空間內存中。
當程序結束時,python只會回收自己的內存空間,即用戶態內存,而操作系統的資源則沒有被回收,這就需要我們定制__del__,在對象被刪除前向操作系統發起關閉數據庫鏈接的系統調用,回收資源
這與文件處理是一個道理:
f=open('a.txt') #做了兩件事,在用戶空間拿到一個f變量,在操作系統內核空間打開一個文件
del f #只回收用戶空間的f,操作系統的文件還處于打開狀態#所以我們應該在del f之前保證f.close()執行,即便是沒有del,程序執行完畢也會自動del清理資源,于是文件操作的正確用法應該是
f=open('a.txt')
讀寫...
f.close()
很多情況下大家都容易忽略f.close,這就用到了with上下文管理
?
十、__enter__和__exit__
我們知道在操作文件對象的時候可以這么寫
with open('a.txt') as f:'代碼塊'
上述叫做上下文管理協議,即with語句,為了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法。
?
1.上下文管理協議
class Open:def __init__(self, name):self.name = namedef __enter__(self):print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')return selfdef __exit__(self, exc_type, exc_val, exc_tb):print('with中代碼塊執行完畢時執行__exit__')with Open('a.txt') as f:print('=====>執行代碼塊')print(f,f.name)>>>
出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量
=====>執行代碼塊
<__main__.Open object at 0x0000013191B98710> a.txt
with中代碼塊執行完畢時執行__exit__
?
2. __exit__()中的三個參數分別代表 異常類型,異常值 和 追溯信息,with語句中代碼塊出現異常,則with后的代碼都無法執行。
class Open:def __init__(self, name):self.name = namedef __enter__(self):print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')def __exit__(self, exc_type, exc_val, exc_tb):print('with中代碼塊執行完畢時執行__exit__')print(exc_type)print(exc_val)print(exc_tb)with Open('a.txt') as f:print('=====>執行代碼塊')raise AttributeError('***著火啦,救火啊***')
print('0'*100) # ------------------------------->不會執行
?
3. 如果__exit()返回值為True,那么異常會被清空,就好像啥都沒發生一樣,with中的代碼將不會繼續執行,而with后的語句正常執行。
class Open:def __init__(self, name):self.name = namedef __enter__(self):print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')def __exit__(self, exc_type, exc_val, exc_tb):print('with中代碼塊執行完畢時__exit__')print(exc_type)print(exc_val)print(exc_tb)return Truewith Open('a.txt') as f:print('=====>執行代碼塊')raise AttributeError('***著火啦,救火啊***')
print('0'*100) #------------------------------->會執行
?
4.模擬open
class Open():def __init__(self, filename, mode='r', encoding='utf-8'):self.file = open(filename, mode, encoding=encoding)def __enter__(self):return self.filedef __exit__(self, exc_type, exc_val, exc_tb):self.file.close()return Truedef __getattr__(self, item):return getattr(self.file, item)with Open('a.txt','w') as f:print(f)f.write('aaaaaa')f.wasdf #拋出異常,交給__exit__處理
5. 總結:
with obj as f:'代碼塊'1. with obj -----> 觸發obj.__enter__(), 拿到返回值2. as f -----> f = 返回值3.with obj as f 等同于 f = obj.__enter__()4. 執行代碼塊
一:沒有異常的情況下,整個代碼塊運行完畢后去觸發__exit__(),它的三個參數都為空
二:有異常的情況下,從異常出先的位置直接觸發__exit__a:如果__exit__()返回值為Ture,代表吞掉了異常,with中的代碼終止運行, with后的代碼正常運行b:如果__exit__()的返回值不為Ture,代表吐出了異常c:__exit__的運行完畢就代表了整個with語句的執行完畢
?
6.?用途或者說好處:
1.使用with語句的目的就是把代碼塊放入with中執行,with結束后,自動完成清理工作,無須手動干預。
2.在需要管理一些資源比如文件,網絡連接和鎖的編程環境中,可以在__exit__中定制自動釋放資源的機制,你無須再去關系這個問題,這將大有用處。
?
十一、__call__
對象后面加括號,觸發執行。
注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;
而對于 __call__ 方法的執行是由對象后加括號觸發的,即:對象() 或者 類()
class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 執行 __init__
obj() # 執行 __call__
?
十二、__next__和__iter__實現迭代器協議
1. 簡單示范
class Foo:def __init__(self, x):self.x = xdef __iter__(self):return selfdef __next__(self):self.x += 1if self.x > 6:raise StopIterationreturn self.xf = Foo(3)for i in f:print(i)
?
2.?練習:簡單模擬range,加上步長
class Range:def __init__(self, start, end, step):self.start = startself.end = endself.step = stepdef __iter__(self):return selfdef __next__(self):self.start += self.stepif self.start > self.end:raise StopIterationreturn self.startr = Range(1,10,2)for i in r:print(i)>>>
3
5
7
9
?
3. 實現斐波那契數列
class Fib:def __init__(self):self.a = 1self.b = 1def __iter__(self):return selfdef __next__(self):self.a, self.b = self.b, self.a + self.bif self.a > 15:raise StopIterationreturn self.af = Fib()for i in f:print(i)
?