python类高级用法_十.python面向对象高级用法
1.反射
1.1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了計算機科學領域關于應用反射性的研究。它首先被程序語言的設計領域所采用,并在Lisp和面向對象方面取得了成績。
1.2 反射的四個方法
1.hasattr(object,name)
判斷object中有沒有一個name字符串對應的方法或屬性
2.getattr(object, name, default=None)
獲取屬性,獲取object中是否有name屬性,如果有則獲取,如果沒有則報錯,如果設置default則返回default的值,不會報錯。
3.setattr(object, k, v)
設置屬性,設置object的屬性,k為設置屬性的鍵,v為設置屬性的值。
4.delattr(object, x)
刪除屬性,刪除object的x屬性。
方法示例:
1 #!/usr/bin/env python
2 #-*- coding: utf-8 -*-
3
4 classHouse:5 def __init__(self,length,width):6 self.length =length7 self.width =width8
9 defArea(self):10 return "房子的面積為%s平方米" %(self.length *self.width)11
12
13 h1 = House(10,20)14 #打印h1的屬性字典
15 print(h1.__dict__)16 #判斷是否有length屬性(返回bool值)
17 print(hasattr(h1,'length')) #切記length屬性是字符串類型******
18 #獲取length屬性(返回獲取到的值,default默認是不存在,獲取不到返回"不存在")
19 print(getattr(h1,'lengtha','不存在'))20 #設置屬性
21 print(setattr(h1,'height','30'))22 #設置完查看
23 print(h1.__dict__)24 #刪除屬性(刪除不存在的會報錯,AttributeError)
25 delattr(h1,'length')26 #刪除完查看
27 print(h1.__dict__)
屬性方法示例
結果:
1 {'length': 10, 'width': 20}2 True3 不存在4 None5 {'length': 10, 'width': 20, 'height': '30'}6 {'width': 20, 'height': '30'}
View Code
1.3 為什么用反射
可以事先定義好接口,接口只有在被完成后才會真正執行,這實現了即插即用,這其實是一種‘后期綁定’,什么意思?即你可以事先把主要的邏輯寫好(只定義接口),然后后期再去實現接口的功能。
實現可插拔機制
示例:
A程序員
1 #!/usr/bin/env python
2 #-*- coding: utf-8 -*-
3
4 #A程序員寫文件的方法,有讀寫操作,但目前只寫了讀的方法。
5 classFile:6 defread(self):7 print("讀操作")8
9 #def write(self):
10 #print("寫操作")
B程序員(B程序員不用關心A程序員是否寫write方法,利用反射判斷屬性并執行,效率高)
#!/usr/bin/env python#-*- coding: utf-8 -*-
from test importFile
f1=File()if hasattr(f1,'write'):
f1= hasattr(f1,'write')
f1()else:print("屬性不存在繼續寫邏輯")
2.類的特殊方法
2.1 對象的屬性操作
__setattr__ 添加/修改屬性會觸發它的執行
1 #__setattr__ 添加/修改屬性會觸發它的執行
2 classFoo:3 x=1
4 def __init__(self,y):5 self.y=y6
7 def __setattr__(self, key, value):8 print('----> from setattr')9 #以下模擬了給對象添加屬性的原理,重寫了對象添加屬性。
10 #self.key=value #模擬添加屬性,這樣做會導致遞歸。
11 self.__dict__[key]=value #這樣做不會遞歸
12
13 f1=Foo(10) #實例化等于添加self.y屬性
14 print(f1.__dict__) #查看其實并未添加,因為當前的__setattr__覆蓋了系統的,以上加了self.__dict__[key]=value,才進行了屬性的添加
15 f1.x = 20 #為對象手動添加屬性
16 print(f1.__dict__)
__setattr__示例
結果:
1 ----> fromsetattr2 {'y': 10}3 ----> fromsetattr4 {'x': 20, 'y': 10}
View Code
__delattr__ 刪除屬性的時候會觸發
1 #__delattr__ 刪除屬性的時候會觸發
2
3 classFoo:4 x=1
5 def __init__(self,y):6 self.y=y7
8 def __delattr__(self, item):9 print('----> from delattr')10 print('----> %s' %item) #item為刪除的值
11 #del self.item #這樣做會無限遞歸
12 self.__dict__.pop(item) #操作屬性字典刪除
13
14 f1 = Foo(10)15 print(f1.__dict__)16 del f1.y #刪除屬性y
17 print(f1.__dict__)
__delattr__示例
結果:
1 {'y': 10}2 ----> fromdelattr3 ---->y4 {}
View Code
__getattr__ 只有在使用點調用屬性且屬性不存在的時候才會觸發
1 #__getattr__ 只有在使用點調用屬性且屬性不存在的時候才會觸發
2
3 classFoo:4 x=1
5 def __init__(self,y):6 self.y=y7
8 def __getattr__(self, item):9 print('----> from getattr:你找的屬性不存在')10
11 f1 = Foo(10)12
13 print(f1.y) #屬性存在的時候不會觸發
14 print(f1.z) #屬性不存在的時候會觸發
__getattr__示例
結果:
1 10
2 ----> fromgetattr:你找的屬性不存在3 None
View Code
__getattribute__ 不管屬性是否存在都會調用
1 #__getattribute__ 在使用點調用屬性就會執行
2
3 classFoo:4 def __init__(self,x):5 self.x=x6
7 def __getattribute__(self, item):8 print('from------>__getattribute__')9
10 f1=Foo(10)11 #__getattribute__與__getattr__不同的是,不管屬性是否存在都會調用。
12 f1.x13 f1.yyy
__getattrbute__示例
結果:
1 from------>__getattribute__
2 from------>__getattribute__
View Code
__getattribute__ 與__getattr__ 同時出現
1 #__getattr__與__getattribute__共存
2
3 classFoo:4 def __init__(self,x):5 self.x=x6
7 def __getattr__(self, item):8 print('from------>__getattr__')9
10 def __getattribute__(self, item):11 print('from ------>__getattribute__')12 #raise AttributeError('沒有這個屬性')
13
14 f1=Foo(10)15 f1.x #共存調用有的x屬性執行__getattribute__
16 f1.yyy #共存調用沒有的yyy屬性也執行__getattribute__
17
18 #總結:當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中拋出異常AttributeError
__getattr__與__getattribute__共存
__getitem__:obj['屬性'] 時觸發
1 #__getitem__:obj['屬性'] 時觸發
2
3 classFoo:4 x=1
5 def __init__(self,y):6 self.y=y7
8 def __getitem__(self, item):9 print('from ----->__getitem__')10 print(self.__dict__[item])11
12 f1 = Foo(10)13
14 print(f1['y'])
__getitem__示例
結果:
1 from ----->__getitem__
2 10
3 None
View Code
__setitem__:obj['屬性']=屬性的值 時觸發
1 #__setitem__:obj['屬性']=屬性的值 時觸發
2 classFoo:3 x=1
4 def __init__(self,y):5 self.y=y6
7 def __setitem__(self, key, value):8 print('from ------>__setitem__')9 self.__dict__[key] =value10
11 f1=Foo(10)12 print(f1.__dict__)13 f1['z'] = 30
14 print(f1.__dict__)
__setitem__示例
結果:
1 {'y': 10}2 from ------>__setitem__
3 {'z': 30, 'y': 10}
View Code
__delitem__:del obj['屬性'] 時觸發
1 #__delitem__:del obj['屬性'] 時觸發
2 classFoo:3 x=1
4 def __init__(self,y):5 self.y=y6
7 def __delitem__(self, key):8 print('from ------>__delitem__')9 self.__dict__.pop(key)10
11 f1=Foo(10)12 print(f1.__dict__)13 del f1['y']14 print(f1.__dict__)
__delitem__示例
結果:
1 {'y': 10}
2 from ------>__delitem__
3 {}
View Code
2.2 檢查對象和子類
isinstance語法:isinstance(obj,cls)
功能:檢查對象obj是否是類cls的實例
1 classFoo(object):2 pass
3
4 obj =Foo()5 print(isinstance(obj, Foo)) #判斷obj是否是Foo的實例
isinstance示例
issubclass語法:issubclass(sub,super)
功能:檢查sub類是否是super類的派生類
1 classFoo(object):2 pass
3
4 classBar(Foo):5 pass
6
7 print(issubclass(Bar,Foo)) #判斷Bar是否是Foo的子類
issubclass示例
2.3 __str__與__repr__
__str__與__repr__是控制輸出的,__str__是控制print輸出的,__repr__是控制控制臺輸出的
str函數或者print函數--->obj.__str__()
repr或者交互式解釋器--->obj.__repr__()
注意:必須以return結尾,并且return的值只能是字符串
1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5 #自定義str
6 def __str__(self):7 return "name is %s,age is %s" %(self.name,self.age)8
9 f1 = Foo('zhangsan',23)10
11 print(f1) #str(f1)--->f1.__str__() #print調用順序
12 #這里不定義__str__會顯示<__main__.Foo object at 0x0000025B3895B668>
13 #定制str會顯示
14 #name is zhangsan,age is 23
__str__示例
1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5 #自定義repr
6 def __repr__(self):7 return "name is %s,age is %s" %(self.name,self.age)8
9 f1 = Foo('zhangsan',23)10
11 print(f1) #repr(f1)--->f1.__repr__() #調用順序
12
13 ###############控制臺示例#####################
14 >>>
15 >>> classFoo:16 ... def __init__(self,name,age):17 ... self.name =name18 ... self.age =age19 ... def __repr__(self):20 ... return "name is %s,age is %s" %(self.name,self.age)21 ...22 >>> f1 = Foo('zhangsan',23)23 >>>f124 name is zhangsan,age is 23
25 >>>
__repr__示例
1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5
6 def __str__(self):7 return ("from---->str")8
9 def __repr__(self):10 return "name is %s,age is %s" %(self.name,self.age)11
12 f1 = Foo('zhangsan',23)13
14 print(f1) #共存調用順序,str(f1)--->f1.__str__()--->f1.__repr__()
__str__與__repr__共存
2.4自定制format
1 format_dic ={2 'ymd':'{0.year} {0.month} {0.day}',3 'y-m-d':'{0.year}-{0.month}-{0.day}',4 'm:d:y':'{0.month}:{0.day}:{0.year}',5 'd/m/y':'{0.day}/{0.month}/{0.year}',6 }7
8 classDate:9 def __init__(self,year,month,day):10 self.year =year11 self.month =month12 self.day =day13
14 def __format__(self, format_spec):15 if not format_spec or format_spec not informat_dic:16 format_spec = 'ymd'
17 fm =format_dic[format_spec]18 returnfm.format(self)19 #return format_dic[format_spec].format(self) #整合
20
21
22 d1 = Date('2017','07','07')23 x = format(d1,'ymd')24 y = format(d1,'y-m-d')25 z = format(d1,'d/m/y')26 print(x)27 print(y)28 print(z)29
30 ##############結果##############
31 2017 07 07
32 2017-07-07
33 07/07/2017
自定義時間格式(__format__)
2.5__slots__
什么是__slots__?
定義:__slots__是一個類變量,變量值可以是列表,元祖,或者可迭代對象,也可以是一個字符串(意味著所有實例只有一個數據屬性)
為什么使用__slots__?
前面學過不管是訪問類的屬性還是實例的屬性其實都是對屬性字典__dict__來做操作,但是類的屬性字典是共享的,實例的屬性字典是獨立的;如果一個類屬性很少,會占用一個字典,而這個類生成的多個實例會生成多個字典,字典一多,就會占用大量的內存,為了節省內存,可以使用__slots__來代替__dict__,這樣所有實例的屬性字典就會是一個__slots__,減少了內存的占用。
使用__slots__的優缺點。
優點:節省內存空間。
缺點:不能給實例添加新的屬性,如果添加,只能在定義__slots__里面定義。
__slots__應該用在哪?
__slots__的很多特性都依賴于普通的基于字典的實現。另外,定義了__slots__后的類不再支持一些普通類特性了,比如多繼承。大多數情況下,你應該只在那些經常被使用到的用作數據結構的類上定義__slots__比如在程序中需要創建某個類的幾百萬個實例對象 。
__slots__使用誤區
關于__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。盡管使用__slots__可以達到這樣的目的,但是這個并不是它的初衷。更多的是用來作為一個內存優化工具。
1 classFoo:2 __slots__ = 'name' #定義單個屬性,沒有定義后面就不能添加。
3 #__slots__ = ['name','age'] #定義多個屬性,沒有定義后面就不能添加。
4
5 f1 =Foo()6 f1.name = 'zhangsan'
7 #print(f1.__dict__) #當使用__slots__的時候,就沒有屬性字典了,這里會報錯,(AttributeError: 'Foo' object has no attribute '__dict__')
8 print(f1.__slots__) #name
9 #f1.age = 23 #如果__slots__沒有這個屬性,這里會報錯(AttributeError: 'Foo' object has no attribute 'age')
10
11
12 f2 =Foo()13 f2.name = 'lisi'
14 print(f2.__dict__) #當使用__slots__的時候,就沒有屬性字典了,這里會報錯,(AttributeError: 'Foo' object has no attribute '__dict__')
15 print(f2.__slots__) #name
16
17 #總結:f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省內存
__slots__使用示例
2.6__doc__
__doc__就是文檔描述符,用來描述類或方法的作用,可以任意定義,自己能看懂就好。
特點:無法繼承
1 classFoo:2 '我是描述信息'
3 pass
4
5 classBar(Foo):6 pass
7 print(Bar.__doc__) #該屬性無法繼承給子類
__doc__示例
2.7__module__和__class__
__module__ 表示當前操作的對象在那個模塊
__class__ 表示當前操作的對象的類是什么
創建test/test.py
1 classFoo:2 def __init__(self,name):3 self.name = name
test/test.py
導入test.py模塊
1 from test.test importFoo2
3 obj =Foo()4 print obj.__module__ #輸出 test.test,即:輸出模塊
5 print obj.__class__ #輸出 test.test.Foo,即:輸出類
test.py導入
2.8__del__ 析構方法
定義:析構方法,當對象在內存中被釋放時,自動觸發執行。
1 classFoo:2 def __init__(self,name):3 self.name =name4
5 def __del__(self):6 print("from ------>__del__")7
8 f1 = Foo('zhangsan')9 print('--------------程序執行完------------>')10
11
12 #結果
13 --------------程序執行完------------>
14 from ------>__del__
15
16 #結果可以看出程序執行完畢,觸發析構函數。
示例一
1 classFoo:2 def __init__(self,name):3 self.name =name4
5 def __del__(self):6 print("from ------>__del__")7
8 f1 = Foo('zhangsan')9 delf110 print('--------------程序執行完------------>')11
12 #結果
13 from ------>__del__
14 --------------程序執行完------------>
15
16 #以上可以看出,刪除f1實例,觸發析構函數,然后程序執行完畢。
示例二
注:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
2.9 __call__
對象后面加括號,觸發執行。
1 #!/usr/bin/env python
2 #-*- coding: utf-8 -*-
3
4 classFoo:5 def __init__(self,name):6 self.name =name7
8 def __call__(self, *args, **kwargs):9 print('from------>__call__')10
11 f1 = Foo('zhangsan')12
13
14 f1() #執行Foo下的__call__方法
15 Foo('zhangsan')() #等同于f1(),執行Foo下的__call__方法
16 Foo('zhangsan') #例如Foo()觸發執行的是abc下的__call__方法。
17
18 結果#19 from------>__call__
20 from------>__call__
__call__示例
注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對于 __call__ 方法的執行是由對象后加括號觸發的,即:對象() 或者 類()()
2.10 __next__和__iter__實現迭代器協議
1 classFoo:2 def __init__(self,x):3 self.x=x4
5 def __iter__(self):6 returnself7
8 def __next__(self):9 n=self.x10 self.x+=1
11 returnself.x12
13 f=Foo(3)14 for i inf:15 print(i)16
17 #1.首先要有__iter__,生成迭代器對象的時候會調用
18 #2.要有__next__方法,因為可以被next()或__next__()函數調用并不斷返回下一個值(直到沒有數據時拋出StopIteration錯誤)的對象稱為迭代器
19 #3.強大的for循環遇到StopIteration會自動停止,并且不會報錯。
迭代器實現的基本過程
1 classFoo:2 def __init__(self):3 self.x = 1
4 self.y = 1
5
6 def __iter__(self):7 returnself8
9 def __next__(self):10 self.x,self.y = self.y,self.x +self.y11 if self.x > 20:12 raise StopIteration('異常了。。。')13 returnself.x14
15 f1 =Foo()16
17 #print(f1.__next__())
18 #print(f1.__next__())
19 #print(f1.__next__())
20 #print(f1.__next__())
21 #print(f1.__next__())
22 #print(f1.__next__())
23 #print(f1.__next__())
24 #print(f1.__next__())
25
26 for i inf1:27 print(i)
迭代器實現斐波那契
3.描述符
1.描述符是什么?
描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議;python描述符是一個“綁定行為”的對象屬性,在描述符協議中,它可以通過方法重寫屬性的訪問。如果__get__(),__set__(),__delete__()被定義在一個對象中,這個對象就是一個描述符。
定義描述符
1 class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱作一個描述符(實現任意一個都被稱作描述符)
2 def __get__(self, instance, owner):3 pass
4 def __set__(self, instance, value):5 pass
6 def __delete__(self, instance):7 pass
描述符的定義
2.描述符是做什么的?
描述符的作用是用來代理另外一個類的屬性的(注意:必須把描述符定義成這個類的類屬性,不能定義到構造函數中)
為什么說是代理屬性呢?
1 classFoo:2 def __get__(self, instance, owner):3 print('觸發get')4 def __set__(self, instance, value):5 print('觸發set')6 def __delete__(self, instance):7 print('觸發delete')8
9 #包含這三個方法的新式類稱為描述符,由這個類產生的實例進行屬性的調用/賦值/刪除,并不會觸發這三個方法
10 f1=Foo()11 f1.name='zhangsan'
12 f1.name13 del f1.name
示例:先看描述符類產生的實例進行屬性操作
1 #描述符Str
2 classStr:3 def __get__(self, instance, owner):4 print('Str調用...')5 def __set__(self, instance, value):6 print('Str設置...')7 def __delete__(self, instance):8 print('Str刪除...')9
10 #描述符Int
11 classInt:12 def __get__(self, instance, owner):13 print('Int調用...')14 def __set__(self, instance, value):15 print('Int設置...')16 def __delete__(self, instance):17 print('Int刪除...')18
19 classPeople:20 name=Str() #Str描述符用在這里
21 age=Int() #Int描述符用在這里
22 def __init__(self,name,age): #name被Str類代理,age被Int類代理
23 self.name=name24 self.age=age25
26 #實例化的時候會調用,zhangsan--->name(因為name被Str代理)--->Str--->觸發Str下的__set__方法(因為實例化本身就是把zhangsan賦值給name,是設置操作)
27 #age和name相同只不過是被Int代理。
28 p1=People('zhangsan',23)29 #被代理的屬性(name和age,在示例本身屬性字典里查不到)
30 print(p1.__dict__)31 #實例name屬性被Str代理,調用/賦值/刪除會觸發Str描述符的方法
32 print('------>描述符Str的使用')33 p1.name34 p1.name='zhangsan'
35 #因為name被代理,所以代理里面沒有操作,屬性字典為空
36 print(p1.__dict__)37 delp1.name38 #實例age屬性被Int代理,調用/賦值/刪除會觸發Int描述符的方法
39 print('------>描述符Int的使用')40 p1.age41 p1.age=23
42 #因為name被代理,所以代理里面沒有操作,屬性字典為空
43 print(p1.__dict__)44 delp1.age45 print(type(p1))
示例:再來看描述符代理類的屬性操作
1 Str設置...2 Int設置...3 {}4 ------>描述符Str的使用5 Str調用...6 Str設置...7 {}8 Str刪除...9 ------>描述符Int的使用10 Int調用...11 Int設置...12 {}13 Int刪除...
View Code
總結:描述符就是來代理其他的類的屬性操作的。
3.描述符的種類
描述符分為兩種
1.數據描述符:至少實現了__get__()和__set__()
1 classFoo:2 def __set__(self, instance, value):3 print('set')4 def __get__(self, instance, owner):5 print('get')
數據描述符示例
2.非數據描述符:沒有實現__set__()
1 classFoo:2 def __get__(self, instance, owner):3 print('get')
非數據描述符示例
4.使用描述符要注意的事項*****
1.描述符本身應該定義成新式類,被代理的類也應該是新式類***
2.必須把描述符定義成這個類的類屬性,不能為定義到構造函數中(__init__())***
3. 要嚴格遵循該優先級,優先級由高到底分別是
1) 類屬性
2) 數據描述符
3) 實例屬性
4) 非數據描述符
5) 找不到的屬性觸發__getattr__()
1 #描述符Str,因為至少包含了__get__和__set__,所以具體可以稱為數據描述符。
2 classStr:3 def __get__(self, instance, owner):4 print('Str調用...')5 def __set__(self, instance, value):6 print('Str設置...')7 def __delete__(self, instance):8 print('Str刪除...')9
10 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str類代理,age被Int類代理
13 self.name=name14 self.age=age15
16 #基于上面的演示,我們已經知道,在一個類中定義描述符它就是一個類屬性,存在于類的屬性字典中,而不是實例的屬性字典
17 #那既然描述符被定義成了一個類屬性,直接通過類名也一定可以調用吧,這里做下實驗。
18 People.name #測試通過,可以調用。但是本質就是在調用描述符Str,觸發了__get__(),但并不能證明類(屬性>數據描述符)
19 #再來看賦值和刪除
20 People.name = 'zhangsan' #沒有觸發__set__方法
21 del People.name #沒有觸發__delete__方法
22
23 '''
24 結論:從類對屬性的賦值和刪除的時候,并沒有觸發__set__和__delete__方法,說明類對屬性的操作要高于描述符。25 '''
26
27 類屬性>數據描述符
類屬性>數據描述符
1 #描述符Str,因為至少包含了__get__和__set__,所以具體可以稱為數據描述符。
2 classStr:3 def __get__(self, instance, owner):4 print('Str調用...')5 def __set__(self, instance, value):6 print('Str設置...')7 def __delete__(self, instance):8 print('Str刪除...')9
10 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str類代理
13 self.name=name14 self.age=age15
16 #這里定義一個實例
17 p1=People('zhangsan',23)18
19 #這里用實例對屬性的調用/賦值/刪除來判斷實例屬性和數據描述符的優先級
20 print("開始測試......")21 p1.name22 p1.name='lisi'
23 delp1.name24 '''
25 結果:26 Str設置...27 開始測試......28 Str調用...29 Str設置...30 Str刪除...31
32 結論:數據描述符>實例屬性33 '''
數據描述符>實例屬性
1 #描述符Str,因沒有__set__方法,所以具體可以稱為非數據描述符。
2 classStr:3 def __get__(self, instance, owner):4 print('Str調用...')5
6 classPeople:7 name=Str()8 def __init__(self,name,age): #name被Str類代理
9 self.name=name10 self.age=age11
12 #這里定義一個實例
13 p1=People('zhangsan',23)14
15 #這里用調用示例屬性的方式來判斷實例屬性和非數據描述符的優先級
16 p1.name #并沒有觸發Str的__get__方法。
17 print(p1.name) #可以找到,說明調用的是實例的屬性字典。
18
19 '''
20 結果:zhangsan21 結論:實例屬性>非數據描述符22 '''
實例屬性>非數據描述符
5.描述符的使用
python是弱類型語言,即參數的賦值沒有類型限制,下面我們通過描述符機制來實現類型限制功能
1 classTyped:2 def __init__(self,name,expected_type):3 self.name=name4 self.expected_type=expected_type5
6 def __get__(self, instance, owner):7 if instance isNone:8 returnself9 return instance.__dict__[self.name]10
11 def __set__(self, instance, value):12 if notisinstance(value,self.expected_type):13 raise TypeError('當前類型是:%s 需要改為:%s' %(type(value),str(self.expected_type)))14 instance.__dict__[self.name]=value15
16 def __delete__(self, instance):17 instance.__dict__.pop(self.name)18
19 classPeople:20 name=Typed('name',str)21 age=Typed('age',int)22 salary=Typed('name',float)23 def __init__(self,name,age,salary):24 self.name=name25 self.age=age26 self.salary=salary27
28 #p1=People(123,18,3333.3)
29 #p1=People('zhangsan','18',3333.3)
30 #p1=People('zhangsan',18,3333)
實例化類型限制
以上Typed屬于調用重復的代碼,這里可以用類的裝飾器實現
類的裝飾器分類:
1 defdecorate(cls):2 print('類的裝飾器開始運行啦------>')3 returncls4
5 @decorate #無參:People=decorate(People)
6 classPeople:7 def __init__(self,name,age,salary):8 self.name=name9 self.age=age10 self.salary=salary11
12 p1=People('zhangsan',23,3333.3)
類的裝飾器:無參
1 def typeassert(**kwargs):2 defdecorate(cls):3 print('類的裝飾器開始運行啦------>',kwargs)4 returncls5 returndecorate6 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People)
7 classPeople:8 def __init__(self,name,age,salary):9 self.name=name10 self.age=age11 self.salary=salary12
13 p1=People('zhangsan',23,3333.3)
類的裝飾器:有參
1 classTyped:2 def __init__(self,name,expected_type):3 self.name=name4 self.expected_type=expected_type5 def __get__(self, instance, owner):6 print('get--->',instance,owner)7 if instance isNone:8 returnself9 return instance.__dict__[self.name]10
11 def __set__(self, instance, value):12 print('set--->',instance,value)13 if notisinstance(value,self.expected_type):14 raise TypeError('Expected %s' %str(self.expected_type))15 instance.__dict__[self.name]=value16 def __delete__(self, instance):17 print('delete--->',instance)18 instance.__dict__.pop(self.name)19
20 def typeassert(**kwargs):21 defdecorate(cls):22 print('類的裝飾器開始運行啦------>',kwargs)23 for name,expected_type inkwargs.items():24 setattr(cls,name,Typed(name,expected_type))25 returncls26 returndecorate27 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People)
28 classPeople:29 def __init__(self,name,age,salary):30 self.name=name31 self.age=age32 self.salary=salary33
34 print(People.__dict__)35 p1=People('zhangsan',23,3333.3)
實例化類型限制(裝飾器實現)
6.描述符實戰
6.1 自制@property
1 classLazyproperty:2 def __init__(self,func):3 self.func=func4 def __get__(self, instance, owner):5 print('這是我們自己定制的靜態屬性,r1.area實際是要執行r1.area()')6 if instance isNone:7 returnself8 return self.func(instance) #此時你應該明白,到底是誰在為你做自動傳遞self的事情
9
10 classRoom:11 def __init__(self,name,width,length):12 self.name=name13 self.width=width14 self.length=length15
16 @Lazyproperty #area=Lazyproperty(area) 相當于定義了一個類屬性,即描述符
17 defarea(self):18 return self.width *self.length19
20 r1=Room('alex',1,1)21 print(r1.area)
自制@property
1 classLazyproperty:2 def __init__(self,func):3 self.func=func4 def __get__(self, instance, owner):5 print('這是我們自己定制的靜態屬性,r1.area實際是要執行r1.area()')6 if instance isNone:7 returnself8 else:9 print('--->')10 value=self.func(instance)11 setattr(instance,self.func.__name__,value) #計算一次就緩存到實例的屬性字典中
12 returnvalue13
14 classRoom:15 def __init__(self,name,width,length):16 self.name=name17 self.width=width18 self.length=length19
20 @Lazyproperty #area=Lazyproperty(area) 相當于'定義了一個類屬性,即描述符'
21 defarea(self):22 return self.width *self.length23
24 r1=Room('alex',1,1)25 print(r1.area) #先從自己的屬性字典找,沒有再去類的中找,然后出發了area的__get__方法
26 print(r1.area) #先從自己的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算
實現延遲計算功能
6.2 自制@classmethod
1 classClassMethod:2 def __init__(self,func):3 self.func=func4
5 def __get__(self, instance, owner): #類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身,
6 deffeedback():7 print('在這里添加功能...')8 returnself.func(owner)9 returnfeedback10
11 classPeople:12 name='lisi'
13 @ClassMethod #say_hi=ClassMethod(say_hi)
14 defsay_hi(cls):15 print('你好 %s' %cls.name)16
17 People.say_hi()18
19 p1=People()20 p1.say_hi()21 #疑問,類方法如果有參數呢
22
23 classClassMethod:24 def __init__(self,func):25 self.func=func26
27 def __get__(self, instance, owner): #類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身,
28 def feedback(*args,**kwargs):29 print('在這里添加功能...')30 return self.func(owner,*args,**kwargs)31 returnfeedback32
33 classPeople:34 name='lisi'
35 @ClassMethod #say_hi=ClassMethod(say_hi)
36 defsay_hi(cls,msg):37 print('你好 %s %s' %(cls.name,msg))38
39 People.say_hi('見到你很高興')40
41 p1=People()42 p1.say_hi('見到你很高興')
自制@classmethod
6.3 自制@staticmethod
1 classStaticMethod:2 def __init__(self,func):3 self.func=func4
5 def __get__(self, instance, owner): #類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身,
6 def feedback(*args,**kwargs):7 print('在這里可以加功能啊...')8 return self.func(*args,**kwargs)9 returnfeedback10
11 classPeople:12 @StaticMethod#say_hi=StaticMethod(say_hi)
13 defsay_hi(x,y,z):14 print('------>',x,y,z)15
16 People.say_hi(1,2,3)17
18 p1=People()19 p1.say_hi(4,5,6)
自制@staticmethod
7.描述符總結
描述符是可以實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性
描述父是很多高級庫和框架的重要工具之一,描述符通常是使用到裝飾器或者元類的大型框架中的一個組件.
4.上下文管理器
4.1 什么是上下文管理器?
在使用Python編程中,可以會經常碰到這種情況:有一個特殊的語句塊,在執行這個語句塊之前需要先執行一些準備動作;當語句塊執行完成后,需要繼續執行一些收尾動作。這就是上下文管理器。
4.2 為什么要使用?用在哪?
示例1:
當需要操作文件或數據庫的時候,首先需要獲取文件句柄或者數據庫連接對象,當執行完相應的操作后,需要執行釋放文件句柄或者關閉數據庫連接的動作。
示例2:
當多線程程序需要訪問臨界資源的時候,線程首先需要獲取互斥鎖,當執行完成并準備退出臨界區的時候,需要釋放互斥鎖。
對于這些情況,Python中提供了上下文管理器(Context Manager)的概念,可以通過上下文管理器來定義/控制代碼塊執行前的準備動作,以及執行后的收尾動作。
4.3 上下文管理協議又是什么?
當我們需要創建一個上下文管理器類型的時候
必須要實現:
__enter__方法(上文)
__exit__方法(下文)
這對方法就稱為上下文管理協議(Context Manager Protocol),定義了一種運行時上下文環境。
4.4 如何使用上下文管理?
這里利用文件操作with open 來理解上下文管理的用法。
我們在操作文件對象的時候可以這可寫,如下
1 with open('a.txt') as f:2 '代碼塊'
這里的with open('a.txt')代表的意思是打開一個文件句柄,as f的意思是賦值給f,這個過程就是上下文管理協議。
這里來模擬with open的實現,來理解如何使用。
1 '''
2 with obj as f 做的是f = obj.__enter__(),with obj 是觸發obj.__enter__拿到返回值,as f是賦值給f3 執行代碼塊:4 1.沒有異常的情況,整個代碼塊執行完成后觸發__exit__方法,它的三個參數都為None5 2.有異常的情況,會從異常出現的位置直接觸發__exit__的運行、6 a:如果__exit__的返回值為True,代表吞掉異常7 b:如果__exit__的返回值不為True,代表吐出異常8 c:__exit__的運行完畢就代表了整個with語句的執行完畢。9
10 '''
11 #沒有異常
12 classOpen1:13
14 def __init__(self,name):15 self.name =name16
17 def __enter__(self):18 print("執行enter")19 returnself20
21 def __exit__(self, exc_type, exc_val, exc_tb):22 print("執行exit")23 print("參數開始------------------>")24 print(exc_type)25 print(exc_val)26 print(exc_tb)27 print("參數結束------------------>")28
29 with Open1('a.txt') as f:30 pass
31
32 print('='*50)33
34 #有異常
35 classOpen:36
37 def __init__(self,name):38 self.name =name39
40 def __enter__(self):41 print("執行enter")42 returnself43
44 def __exit__(self, exc_type, exc_val, exc_tb):45 print("執行exit")46 print("參數開始------------------>")47 print(exc_type)48 print(exc_val)49 print(exc_tb)50 print("參數結束------------------>")51 #return True #這里代表吞掉異常。開啟就會吞掉異常
52
53 with Open('a.txt') as f:54 raise AttributeError("出錯了。。。")55 print('執行結束。。。。。。。。。。。。。') #如果吞掉異常,這里會執行,如果吐出異常這里不會執行
56
57 模擬with open的實現過程
模擬with open的實現過程
1 classOpen:2 def __init__(self,filepath,mode='r',encoding='utf-8'):3 self.filepath=filepath4 self.mode=mode5 self.encoding=encoding6
7 def __enter__(self):8 #print('enter')
9 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)10 returnself.f11
12 def __exit__(self, exc_type, exc_val, exc_tb):13 #print('exit')
14 self.f.close()15 returnTrue16 def __getattr__(self, item):17 returngetattr(self.f,item)18
19 with Open('a.txt','w') as f:20 print(f)21 f.write('aaaaaa')22 f.asdfsdf #拋出異常,交給__exit__處理
實現一個wtih Open
5.元類(metaclass)
總結
以上是生活随笔為你收集整理的python类高级用法_十.python面向对象高级用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单选选择才可以提交_第二篇:DJANGO
- 下一篇: python数据结构的列表_Python