isinstance(p1,A) 判斷p1是不是類A的對象, 返回布爾值python
issubclass(B,A) 判斷類B是否繼承類A, 即B是不是A的派生類返回布爾值編程
1 class A: 2 pass 3 p1 = A() 4 print(isinstance(p1,A))#判斷p1是不是類A的對象,返回布爾值 5 class B(A): 6 pass 7 print(issubclass(B,A))#判斷類B是否繼承類A,即B是不是A的派生類返回布爾值
主要是指程序能夠訪問, 檢測和修改它自己狀態或行爲的一種能力 (自省)網絡
python面向對象中的反射:經過字符串的形式操做對象相關的屬性. python中的一切事物都是 對象(均可以使用反射)app
四個能夠實現自省的函數:ide
hasattr(p1,"name") 檢測對象是否含有某屬性, 返回布爾值函數
getattr(p1,"name") 獲取對象的某個屬性,不存在會報錯,可定義第三個參數, 若不存在返回定 義值優化
setattr(p1,"salary",1000) 設置屬性, 若已存在會修改spa
delattr(p1,"salary") 刪除對象的某個屬性, 不存在會報錯代理
1 class Zixing: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 def fangfa(self): 6 print("【%s】的年齡是%s"%(self.name,self.age)) 7 p1 = Zixing("alex",20) 8 print(hasattr(p1,"name"))#檢測對象是否含有某屬性,返回布爾值 9 print(hasattr(p1,"fangfa")) 10 11 n = getattr(p1,"name")#獲取對象的某個屬性,不存在會報錯,可定義第三個參數,若不存在返回定義值 12 print(n) 13 14 setattr(p1,"salary",1000)#設置屬性,若已存在會修改 15 print(p1.__dict__) 16 17 delattr(p1,"salary")#刪除對象的某個屬性,不存在會報錯 18 delattr(p1,"aaa") 19 print(p1.__dict__)
好處一: 經過反射, 能夠實現即插即用, 定義接口的功能(能夠事先把主要的邏輯寫好(只定義接code
口), 而後後期去實現接口的功能)
好處二: 動態導入模塊 (基於反射當前模塊成員)
1 import importlib 2 m = importlib.import_module("text.text1")#調用importlib模塊導入的字符串路徑文件結果將導入到指定文件路徑 3 module_t = __import__("text.text1")#__import__方法導入字符串路徑文件將只導入到最頂層文件處 4 print(module_t)#<module 'text' (namespace)> 5 print(m)#<module 'text.text1' from 'D:\\pycharm\\S3\\text\\text1.py
__getattr__:當用點在調用屬性的時候,若是屬性不存在會觸發自定義的__getattr__方法,__getattribute__方法優先於__getattr__,只要一調用就會觸發,只不過在找不到的狀況下會交給__getattr__處理(不經常使用)
1 class C: 2 def __init__(self,x): 3 self.x = x 4 def __getattr__(self, item):#若沒找到調用的屬性會觸發 5 print("執行getattr!") 6 def __getattribute__(self, item):#當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError 7 print("執行getattribute!") 8 raise AttributeError("拋出異常了!") 9 f = C(1) 10 print(f.x) 11 f.y
__setattr__: 在設置值的時候會觸發
1 class D: 2 def __init__(self,name): 3 self.name = name 4 def __setattr__(self, key, value): 5 print("__setattr__工做了!") 6 self.key = value # 這就無限遞歸了,由於這是賦值操做又會立刻觸發__setattr__方法,錯誤用法 7 self.__dict__[key]=value#正確用法,由於設置值底層就是在操做實例屬性字典 8 p1 = D("tom")
__delattr__ : 在進行刪除屬性操做的時候會觸發
1 class D: 2 def __init__(self,name): 3 self.name = name 4 def __delattr__(self, item): 5 print("__delattr__工做了!") 6 #del self.item # 這就無限遞歸了,由於這是刪除操做又會立刻觸發__delattr__方法,錯誤用法 7 self.__dict__.pop(item)#正確用法,由於設置值底層就是在操做實例屬性字典 8 p1 = D("alex") 9 print(p1.__dict__) 10 del p1.name 11 print(p1.__dict__)
包裝: python爲你們提供了標準數據類型, 以及豐富的內置方法, 其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型, 新增/改寫方法, 這就用到了繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)
1 包裝 2 class List(list): 3 def append(self, p_object): 4 if type(p_object) is str: 5 super().append(p_object) 6 else: 7 print("數據不合法!") 8 def mid_shuzi(self): 9 index = int(len(self)/2) 10 print(self[index]) 11 li = List("aaa") 12 li.append(123) 13 print(li) 14 li = List("hello") 15 li.mid_shuzi()
受權: 受權是包裝的一個特性, 包裝一個類型是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能.其餘的則保持原樣. 受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性
實現受權的關鍵點就是覆蓋__getattr__方法
1 import time 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 self.file=open(filename,mode,encoding=encoding) 5 def write(self,line): 6 t=time.strftime('%Y-%m-%d %T') 7 self.file.write('%s %s' %(t,line)) 8 9 def __getattr__(self, item): 10 return getattr(self.file,item) 11 12 f1=FileHandle('b.txt','w+') 13 f1.write('你好啊') 14 f1.seek(0) 15 print(f1.read()) 16 f1.close()
1 class Item: 2 def __getitem__(self, item): 3 print("執行getitem!") 4 return self.__dict__[item] 5 def __setitem__(self, key, value): 6 print("執行setitem!") 7 self.__dict__[key]=value 8 9 def __delitem__(self, key): 10 print("執行delitem!") 11 self.__dict__.pop(key) 12 p1 = Item() 13 #p1.name = "tom" 14 #print(p1.name) 15 p1["name"]="tom"#用字典格式處理實例,會觸發...item內置方法,以.方式調用觸發attr方法 16 print(p1.__dict__) 17 print(p1["name"])
1 class Str: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 def __str__(self):#必須有返回值,且爲字符串 6 print("__str__運行結果以下:") 7 return "名字是%s,年齡是%s"%(self.name,self.age) 8 def __repr__(self):#必須有返回值,且爲字符串 9 print("__repr__運行結果以下:") 10 return "名字是%s,年齡是%s" % (self.name, self.age) 11 p1 = Str("tom",18) 12 print(p1)#print運行本質是str(p1)>>>__str(p1)___>>>__repr(p1),二者並存時,運行__str(p1),兩者返回值必須是字符串,在解釋器中直接先調用__repr__方法
__format__:在用format定義輸出格式時,會觸發__format__方法
1 dic = { 2 "ymd":"{0.year}{0.month}{0.day}", 3 "y-m-d":"{0.year}-{0.month}-{0.day}", 4 "y:m:d":"{0.year}:{0.month}:{0.day}" 5 } 6 class Riqi: 7 def __init__(self,year,month,day): 8 self.year = year 9 self.month = month 10 self.day = day 11 def __format__(self, format_spec): 12 f1 = dic[format_spec] 13 return f1.format(self) 14 p1 = Riqi(2016,12,26) 15 print(format(p1,"ymd"))#自定義打印值的格式,經過format觸發內部的__format__方法 16 print(format(p1,"y-m-d")) 17 print(format(p1,"y:m:d"))
1 class Foo: 2 #__slots__ = "x" 3 __slots__ = ["name","age"] 4 p1 = Foo() 5 p1.x = "tom"#根據類裏__slots__定義的key來傳值,一一對應 6 p1.name = "eric"#類裏__slots__沒有定義name這個key,沒法添加 7 print(p1.x) 8 print(p1.__dict__)#報錯,當類裏定義了__slots__方法後,實例對象再也不有__dict__方法再也不有屬性字典, 9 10 class Foo: 11 __slots__ = ["name","age"]#設置實例傳入的值對應的key 12 p1 = Foo() 13 p1.name = "eric" 14 #p1.age = 18 15 print(p1.name) 16 #print(p1.age)
1 class A: 2 "我是描述信息" 3 pass 4 p1 = A() 5 print(p1.__doc__)#打印類描敘信息 6 class B(A): 7 pass 8 v1 = B() 9 print(v1.__doc__)#類的描述信息沒法被繼承
1 class A: 2 def __init__(self,n): 3 self.n = n 4 def __iter__(self):#遵循迭代器協議 5 return self 6 def __next__(self): 7 if self.n == 5: 8 raise StopIteration("終止了")#拋出異常 9 self.n+= 1 10 return self.n 11 P1 = A(1) 12 print(P1.__next__())#先調用__iter__方法,在調用__next__方法 13 print(P1.__next__()) 14 print(P1.__next__()) 15 print(P1.__next__()) 16 print(P1.__next__()) 17 for i in P1:#for本質先調用__itet__方法,變成可迭代對象,再調用__next__方法,循環打印,捕捉異常退出 18 print(i)
__module__ 表示當前操做的對象在哪一個模塊
__class__ 表示當前操做的對象的類是什麼
1 class A: 2 pass 3 p1 = A() 4 print(p1.__module__)#__main__#輸出對象所在的模塊 5 print(p1.__class__)#<class '__main__.A'>#輸出對象所屬的類
__del__: 結構方法, 當對象在內存中被釋放時, 自動觸發執行. 此方法通常無須定義,結構函數
調用是由解釋器在進行垃圾回收時自動觸發執行的
1 class A: 2 def __init__(self,name): 3 self.name = name 4 def __del__(self): 5 print("我執行了!") 6 p1 = A("tom") 7 print(p1)#當打印完時內存釋放,便會觸發__del__方法
對象後面加括號, 觸發執行
注: 構造方法的執行是由建立對象觸發的, 即: 對象 = 類名() ; 而對於__call__方法的執行是由
對象後加括號觸發的, 即: 對象() 或者 類()()
1 class Foo: 2 3 def __init__(self): 4 pass 5 6 def __call__(self, *args, **kwargs): 7 8 print('__call__') 9 10 11 obj = Foo() # 執行 __init__ 12 obj() # 執行 __call__
描述符本質上就是一個新式類, 在這個新式類中, 至少實現了
__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議
__get__():調用一個屬性時,觸發
__set__():爲一個屬性賦值時,觸發
__delete__():採用del刪除屬性時,觸發
1 定義描述符 2 class Miaoshu: 3 def __get__(self, instance, owner): 4 print("這是__get__描述符") 5 def __set__(self, instance, value): 6 print("這是__set__描述符") 7 def __delete__(self, instance): 8 print("這是__delete__描述符")
描述符是幹什麼的:描述符的做用是用來代理另一個類的屬性的(必須把描述符定義成這個類
的類屬性,不能定義到構造函數中), 描述符只能用來代理除描述符外的其餘類
1 描述符 2 class Miaoshu: 3 def __get__(self, instance, owner): 4 print("這是__get__描述符") 5 def __set__(self, instance, value): 6 print("這是__set__描述符") 7 def __delete__(self, instance): 8 print("這是__delete__描述符") 9 class A: 10 x= Miaoshu()#A類的x屬性被描述符代理了 11 def __init__(self,x): 12 self.x = x
描述符分爲兩種:(1)數據描述符:至少實現了__get__()和__set__()
(2)非數據描述符:沒有實現__set__()
注意事項:
一 描述符自己應該定義成新式類,被代理的類也應該是新式類
二 必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中
三 要嚴格遵循該優先級,優先級由高到底分別是
1.類屬性
2.數據描述符
3.實例屬性
4.非數據描述符
5.找不到的屬性觸發__getattr__()
描述符的使用:
(1)因爲python是弱類型語言, 類型限制
class Daili: def __init__(self,key,expect_type): self.key = key self.expect_type = expect_type def __get__(self, instance, owner): print("觸發__get__") return instance.__dict__(self.key) def __set__(self, instance, value): print("觸發__set__") if not isinstance(value,self.expect_type): raise TypeError("%s的類型不是%s"%(self.key,self.expect_type)) instance.__dict__[self.key] = value def __delete__(self, instance): instance.__dict__.pop(self.key) class A: name = Daili("name",str) age = Daili("age",int) def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = A("tom","123",100)#因爲name被代理,且代理的類屬性被數據描述符代理,因此name的賦值會觸發__set__方法 print(p1.__dict__)#因爲代理中__set__沒有對name進行賦值操做,因此實例p1的屬性字典中不存在name這個屬性
(2)實現裝飾器功能
1 class Daili: 2 def __init__(self,key,expect_type): 3 self.key = key 4 self.expect_type = expect_type 5 def __get__(self, instance, owner): 6 print("觸發__get__") 7 return instance.__dict__(self.key) 8 def __set__(self, instance, value): 9 print("觸發__set__") 10 if not isinstance(value,self.expect_type): 11 raise TypeError("%s的類型不是%s"%(self.key,self.expect_type)) 12 instance.__dict__[self.key] = value 13 def __delete__(self, instance): 14 instance.__dict__.pop(self.key) 15 16 def zsq(**kwargs): 17 def foo(obj): 18 for key,value in kwargs.items(): 19 setattr(obj,key,Daili(key,value))#設置類的屬性 20 return obj 21 return foo 22 @zsq(name = str,age = int) 23 class A: 24 name = Daili("name",str) 25 age = Daili("age",int) 26 def __init__(self,name,age,salary): 27 self.name = name 28 self.age = age 29 self.salary = salary 30 p1 = A("tom",123,100)#因爲name被代理,且代理的類屬性被數據描述符代理,因此name的賦值會觸發__set__方法 31 print(p1.name)#因爲name被代理,且代理的類屬性被數據描述符代理,因此調用name屬性會觸發__get__方法
描述符是能夠實現包括@classmethod,@staticmethod,@property甚至是__slots__屬性
實現@property功能
1 class A: 2 def __init__(self,fun): 3 self.fun = fun 4 def __get__(self, instance, owner): 5 print("__get__") 6 print(instance) 7 print(self.fun.__name__) 8 setattr(instance,self.fun.__name__,self.fun(instance)) 9 return self.fun(instance)#此處的instance爲Edg實例化的對象 10 class Edg: 11 def __init__(self,name,lenth,weith): 12 self.name = name 13 self.lenth = lenth 14 self.weith = weith 15 @A#至關於area = A(area),同時A自己是一個描述符,至關於area被代理了,在調用area時候會觸發描述符裏的__get__方法 16 def area(self): 17 return self.lenth*self.weith 18 P1= Edg("wc",10,20) 19 print(P1.area)#第一次在調用的時候因爲p1屬性字典裏沒有area屬性,故會觸發__get__方法,因爲在__get__方法中對p1屬性字典進行添加 20 # area屬性操做,因此後面再次調用的時候p1會首先從本身的屬性字典中查找area屬性,因爲以前已經設置,因此不會再觸發__get__ 21 #方法,area函數也就不用再次執行,因此能夠節省時間,優化了延遲計算 22 print(P1.area) 23 print(P1.area) 24 # 依次打印 25 # __get__ 26 # <__main__.Edg object at 0x0000000000A45CC0> 27 # area 28 # 200第一次打印 29 # 200 30 # 200
操做文件的方式:
1 with open(xxx,xx,xx) as f: 2 "代碼塊「」
上述叫作上下文管理協議, 即with語句, 爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 return self 6 def __exit__(self, exc_type, exc_val, exc_tb): 7 print("執行__exit__!") 8 print(exc_type) 9 print(exc_val) 10 print(exc_tb) 11 return True#若是在拋出異常狀況下觸發__exit__方法,且返回值爲True,則表明__exit__清除了異常,with函數執行結束,下面代碼繼續執行,不然會中斷執行 12 with Open("a")as f:#with操做觸發__enter__方法,__enter__方法返回值賦值給f 13 print(f) 14 print("======") 15 print("======") 16 print(ajflaj)#打印未定義的變量,觸發異常,觸發__exit__方法,待exit函數執行結束,表明with函數裏的代碼塊也執行結束 17 print("=========") 18 print("*************")
用途或者好處
1.使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工做,無須手動 干預
2.在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在__exit__中定製自動釋放 資源的機制,你無需再去關心這個問題,這將大有用處
python中一切皆是對象,類自己也是一個對象,當使用關鍵字class的時候,python解釋器在加載 class的時候就會建立一個對象(這裏的對象指的是類而非類的實例)
元類是類的類,是類的模板
元類是用來控制如何建立類的,正如類是建立對象的模板同樣
元類的實例爲類,正如類的實例爲對象(f1對象是Foo類的一個實例,Foo類是 type 類的一個實 例)
type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type 類的實例化的對象
建立類的兩種方式
第一種 1 class A: 2 pass
1 第二種 2 def aoo(self): 3 pass 4 x = 1 5 Foo = type("Foo",(object,),{"aoo":aoo,"x":1}) 6 print(Foo)
自制元類
1 class MyType(type): 2 def __init__(self,a,b,c): 3 print('元類的構造函數執行') 4 def __call__(self, *args, **kwargs): 5 obj=object.__new__(self) 6 self.__init__(obj,*args,**kwargs) 7 return obj 8 class Foo(metaclass=MyType): 9 def __init__(self,name): 10 self.name=name 11 f1=Foo('tom')