class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的屬性訪問,觸發__getattr__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('不論是否存在,我都會執行') f1=Foo(10) f1.x f1.xxxxxx
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('不論是否存在,我都會執行') raise AttributeError('哈哈') f1=Foo(10) f1.x f1.xxxxxx #當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError
同__getattr__,__setattr__,__delattr__;python
只是在操做時以字典的方式操做數據庫
class foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self, item): print("觸發__getitem__") return self.__dict__[item] def __setitem__(self, key, value): print("觸發__setitem__") self.__dict__[key] = value def __delitem__(self, key): print("觸發__delitem__") self.__dict__.pop(key) f1 = foo("alex",18) f1["name"] #觸發__getitem__ f1["name"] = "egen" #觸發__setitem__ del f1["name"] #觸發__delitem__
class foo: pass f1 = foo() print(f1) 打印的是f1在哪一個對象中 <__main__.foo object at 0x000002415359D198>
class foo: def __str__(self): return "執行的__str__" f1 = foo() print(f1) print(str(f1)) print(f1.__str__()) 輸出結果: 執行的__str__ 執行的__str__ 執行的__str__ 實際上print(f1)內部作的就是print(str(f1))=print(f1.__str__()) 注意__str__的返回值必須是字符串
__repr__同__str__數組
__repr__應用在解釋器中顯示數據結構
class foo: def __str__(self): return "執行的__str__" def __repr__(self): return "執行的__repr__" f1 = foo() print(f1) #執行的__str__ --------------------------------------------------------------------------- class foo: # def __str__(self): # return "執行的__str__" def __repr__(self): return "執行的__repr__" f1 = foo() print(f1) #執行的__repr__ #注意print(f1)默認作的是str(f1),只有在找不到的時候纔會用__repr__代替
format_dic = { "ymd":"{0.year}{0.mon}{0.day}", "y:m:d":"{0.year}:{0.mon}:{0.day}", "m-d-y":"{0.mon}-{0.day}-{0.year}", } class Date: def __init__(self,year,mon,day): self.year = year self.mon = mon self.day = day def __format__(self, format_spec): # 判斷沒有指定格式或格式指定錯誤狀況下按照默認格式執行 if not format_spec or format_spec not in format_dic: format_spec = "ymd" fm = format_dic[format_spec] return fm.format(self) f1 = Date(2018,12,14) print(format(f1))#無參數 print(format(f1,"m-d-y"))#有參數 print(format(f1,"m-y:d"))#錯誤參數 20181214 12-14-2018 20181214
''' 1.__slots__是什麼:是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性) 2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的) 3.爲什麼使用__slots__:字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__ 當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個 字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給 實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。 4.注意事項:__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該 只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。 關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。 更多的是用來做爲一個內存優化工具。 ''' class Foo: __slots__='x' f1=Foo() f1.x=1 f1.y=2#報錯 print(f1.__slots__) #f1再也不有__dict__ class Bar: __slots__=['x','y'] n=Bar() n.x,n.y=1,2 n.z=3#報錯
class Foo: __slots__=['name','age'] f1=Foo() f1.name='alex' f1.age=18 print(f1.__slots__) f2=Foo() f2.name='egon' f2.age=19 print(f2.__slots__) print(Foo.__dict__) #f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省內存
class Foo: '我是描述信息' pass print(Foo.__doc__)
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Bar.__doc__) #該屬性沒法繼承給子類
__module__ 表示當前操做的對象在那個模塊框架
__class__ 表示當前操做的對象的類是什麼ide
class C: def __init__(self): self.name = ‘SB'
from lib.aa import C obj = C() print obj.__module__ # 輸出 lib.aa,即:輸出模塊 print obj.__class__ # 輸出 lib.aa.C,即:輸出類
析構方法,當對象在內存中被釋放時,自動觸發執行。函數
注:若是產生的對象僅僅只是python程序級別的(用戶級),那麼無需定義__del__,若是產生的對象的同時還會向操做系統發起系統調用,即一個對象有用戶級與內核級兩種資源,好比(打開一個文件,建立一個數據庫連接),則必須在清除對象的同時回收系統資源,這就用到了__del__工具
class foo: def __init__(self,name): self.name = name def __del__(self): print("我是析構方法") f1 = foo("alex") del f1 print("------------") #輸出結果 我是析構方法 ------------
典型的應用場景:優化
建立數據庫類,用該類實例化出數據庫連接對象,對象自己是存放於用戶空間內存中,而連接則是由操做系統管理的,存放於內核空間內存中spa
當程序結束時,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上下文管理
對象後面加括號,觸發執行。
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執行 __init__ obj() # 執行 __call__
__author__ = 'Linhaifeng' class Foo: def __init__(self,x): self.x=x def __iter__(self): return self def __next__(self): n=self.x self.x+=1 return self.x f=Foo(3) for i in f: print(i)
class Foo: def __init__(self,start,stop): self.num=start self.stop=stop def __iter__(self): return self def __next__(self): if self.num >= self.stop: raise StopIteration n=self.num self.num+=1 return n f=Foo(1,5) from collections import Iterable,Iterator print(isinstance(f,Iterator)) for i in Foo(1,5): print(i)
class Foo: def __init__(self): self._a = 1 self._b = 1 def __iter__(self): return self def __next__(self): #a,b = b,a if self._b > 100: raise StopIteration("終止運行") self._a,self._b = self._b,self._a + self._b return self._a f1 = Foo() for i in f1: print(i)
1 描述符是什麼:描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議
__get__():調用一個屬性時,觸發
__set__():爲一個屬性賦值時,觸發
__delete__():採用del刪除屬性時,觸發
class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱做一個描述符 def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass
2 描述符是幹什麼的:描述符的做用是用來代理另一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)
class Foo: def __get__(self, instance, owner): print('觸發get') def __set__(self, instance, value): print('觸發set') def __delete__(self, instance): print('觸發delete') #包含這三個方法的新式類稱爲描述符,由這個類產生的實例進行屬性的調用/賦值/刪除,並不會觸發這三個方法 f1=Foo() f1.name='egon' f1.name del f1.name #疑問:什麼時候,何地,會觸發這三個方法的執行
#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int調用') def __set__(self, instance, value): print('Int設置...') def __delete__(self, instance): print('Int刪除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str類代理,age被Int類代理, self.name=name self.age=age #何地?:定義成另一個類的類屬性 #什麼時候?:且看下列演示 p1=People('alex',18) #描述符Str的使用 p1.name p1.name='egon' del p1.name #描述符Int的使用 p1.age p1.age=18 del p1.age #咱們來瞅瞅到底發生了什麼 print(p1.__dict__) print(People.__dict__) #補充 print(type(p1) == People) #type(obj)實際上是查看obj是由哪一個類實例化來的 print(type(p1).__dict__ == People.__dict__)
3 描述符分兩種
一 數據描述符:至少實現了__get__()和__set__()
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get')
二 非數據描述符:沒有實現__set__()
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
4 注意事項:
一 描述符自己應該定義成新式類,被代理的類也應該是新式類
二 必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中
三 要嚴格遵循該優先級,優先級由高到底分別是
1.類屬性
2.數據描述符
3.實例屬性
4.非數據描述符
5.找不到的屬性觸發__getattr__()
#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class People: name=Str() def __init__(self,name,age): #name被Str類代理,age被Int類代理, self.name=name self.age=age #基於上面的演示,咱們已經知道,在一個類中定義描述符它就是一個類屬性,存在於類的屬性字典中,而不是實例的屬性字典 #那既然描述符被定義成了一個類屬性,直接經過類名也必定能夠調用吧,沒錯 People.name #恩,調用類屬性name,本質就是在調用描述符Str,觸發了__get__() People.name='egon' #那賦值呢,我去,並無觸發__set__() del People.name #趕忙試試del,我去,也沒有觸發__delete__() #結論:描述符對類沒有做用-------->傻逼到家的結論 ''' 緣由:描述符在使用時被定義成另一個類的類屬性,於是類屬性比二次加工的描述符假裝而來的類屬性有更高的優先級 People.name #恩,調用類屬性name,找不到就去找描述符假裝的類屬性name,觸發了__get__() People.name='egon' #那賦值呢,直接賦值了一個類屬性,它擁有更高的優先級,至關於覆蓋了描述符,確定不會觸發描述符的__set__() del People.name #同上 '''
#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class People: name=Str() def __init__(self,name,age): #name被Str類代理,age被Int類代理, self.name=name self.age=age p1=People('egon',18) #若是描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操做,於p1自己無關了,至關於覆蓋了實例的屬性 p1.name='egonnnnnn' p1.name print(p1.__dict__)#實例的屬性字典中沒有name,由於name是一個數據描述符,優先級高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了 del p1.name
class Foo: def func(self): print('我胡漢三又回來了') f1=Foo() f1.func() #調用類的方法,也能夠說是調用非數據描述符 #函數是一個非數據描述符對象(一切皆對象麼) print(dir(Foo.func)) print(hasattr(Foo.func,'__set__')) print(hasattr(Foo.func,'__get__')) print(hasattr(Foo.func,'__delete__')) #有人可能會問,描述符不都是類麼,函數怎麼算也應該是一個對象啊,怎麼就是描述符了 #笨蛋哥,描述符是類沒問題,描述符在應用的時候不都是實例化成一個類屬性麼 #函數就是一個由非描述符類實例化獲得的對象 #沒錯,字符串也同樣 f1.func='這是實例屬性啊' print(f1.func) del f1.func #刪掉了非數據 f1.func()
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一個數據描述符,由於name=Foo()而Foo實現了get和set方法,於是比實例屬性有更高的優先級 #對實例的屬性操做,觸發的都是描述符的 r1=Room('廁所',1,1) r1.name r1.name='廚房' class Foo: def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一個非數據描述符,由於name=Foo()而Foo沒有實現set方法,於是比實例屬性有更低的優先級 #對實例的屬性操做,觸發的都是實例本身的 r1=Room('廁所',1,1) r1.name r1.name='廚房'
class Foo: def func(self): print('我胡漢三又回來了') def __getattr__(self, item): print('找不到了固然是來找我啦',item) f1=Foo() f1.xxxxxxxxxxx
5 描述符使用
衆所周知,python是弱類型語言,即參數的賦值沒有類型限制,下面咱們經過描述符機制來實現類型限制功能
class Str: def __init__(self,name): self.name=name def __get__(self, instance, owner): print('get--->',instance,owner) return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary #疑問:若是我用類名去操做屬性呢 People.name #報錯,錯誤的根源在於類去操做屬性時,會把None傳給instance #修訂__get__方法 class Str: def __init__(self,name): self.name=name def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(People.name) #完美,解決
class Str: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): #若是不是指望的類型,則拋出異常 raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name',str) #新增類型限制str def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People(123,18,3333.3)#傳入的name因不是字符串類型而拋出異常
class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Typed('name',str) age=Typed('name',int) salary=Typed('name',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People(123,18,3333.3) p1=People('egon','18',3333.3) p1=People('egon',18,3333)
大刀闊斧以後咱們已然能實現功能了,可是問題是,若是咱們的類有不少屬性,你仍然採用在定義一堆類屬性的方式去實現,low,這時候我須要教你一招:獨孤九劍
def decorate(cls): print('類的裝飾器開始運行啦------>') return cls @decorate #無參:People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,3333.3)
def typeassert(**kwargs): def decorate(cls): print('類的裝飾器開始運行啦------>',kwargs) return cls return decorate @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,3333.3)
終極大招
class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print('類的裝飾器開始運行啦------>',kwargs) for name,expected_type in kwargs.items(): setattr(cls,name,Typed(name,expected_type)) return cls return decorate @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(People.__dict__) p1=People('egon',18,3333.3)
6 描述符總結
描述符是能夠實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性
描述符是不少高級庫和框架的重要工具之一,描述符一般是使用到裝飾器或者元類的大型框架中的一個組件