isinstance(obj,cls) 檢查obj是不是cls的實例python
issubclass(obj,cls) 檢查obj是不是cls的繼承類程序員
經過字符串的形式操做對象的相關的屬性。app
主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力(自省)this
hasattr(obj,name) 檢查obj下是否有叫name屬性,name必須字符串 getattr(obj,name,default=None) 查詢obj下的屬性,有返回name對應的參數,沒有返回default setattr(obj,x,y) 給obj設置屬性,x必須字符串 delattr(obj,x) 刪除obj下的屬性,x必須字符串
示例:code
class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介賣房子啦,傻逼纔買呢,可是誰能證實本身不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('萬成置地','回龍觀天露園') #檢測是否含有某屬性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #獲取屬性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #報錯 print(getattr(b1,'aaaaaaaa','不存在啊')) #設置屬性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #刪除屬性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,則報錯 print(b1.__dict__)
好處一:實現可插拔機制orm
有倆程序員,一個lili,一個是egon,lili在寫程序的時候須要用到egon所寫的類, 可是egon去跟女友度蜜月去了,尚未完成他寫的類,lili想到了反射,使用了反射機制lili能夠繼續完成本身的代碼, 等egon度蜜月回來後再繼續完成類的定義而且去實現lili想要的功能。 總之反射的好處就是,能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用, 這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能
好處二:動態導入模塊(基於反射當前模塊成員)對象
在模塊中導入本身: this_modul=sys.modules[__name__] 使用字符串導入模塊: 一、__import__("time") 二、import importlib importlib.import_module("time") 推薦使用第二種
設置屬性的時候會觸發。實質是操做類.__dict__
刪除類的屬性時會觸發,實質是操做類.__dict__
調用類中不存在的屬性時會觸發。
三者的用法演示:繼承
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的屬性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #這就無限遞歸了,你好好想一想 # self.__dict__[key]=value #應該使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #無限遞歸了 self.__dict__.pop(item) #__setattr__添加/修改屬性會觸發它的執行 f1=Foo(10) print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值 f1.z=3 print(f1.__dict__) #__delattr__刪除屬性的時候會觸發 f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做 del f1.a print(f1.__dict__) #__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發 f1.xxxxxx
做用同上。只不過是調用的時候__setattr__那中方法是用 . 去調用。而這種是用["值"]的方式去調用。遞歸
class Foo: x = 1 def __init__(self, y): self.y = y def __getitem__(self, item): #執行對象[屬性],找不到屬性的時候會執行這個代碼。 print('----> from getattr:你找的屬性不存在') def __setitem__(self, key, value): #執行對象[屬性]=值,的時候會執行這個代碼 self.__dict__[key]=value def __delitem__(self, item): #執行 del 對象[屬性] 的時候會執行這個代碼。 self.__dict__.pop(item) f1 = Foo(10) #初始化觸發init print(f1.__dict__) f1["z"]=3 #賦值觸發setitem print(f1.__dict__) f1["a"] #查找不存在的屬性,觸發getitem f1.__dict__['a'] = 3 del f1["a"] #刪除觸發delitem print(f1.__dict__)
class List(list): #繼承list全部的屬性,也能夠派生出本身新的,好比append和mid def append(self, p_object): if not isinstance(p_object,int): #' 派生本身的append:加上類型檢查' raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增本身的屬性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #報錯,必須爲int類型 print(l.mid) #其他的方法都繼承list的 l.insert(0,-123) print(l) l.clear() print(l)
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item) f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close() #重寫list class List: def __init__(self,seq): self.seq=seq def append(self, p_object): ' 派生本身的append加上類型檢查,覆蓋原有的append' if not isinstance(p_object,int): raise TypeError('must be int') self.seq.append(p_object) def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() @property def mid(self): '新增本身的方法' index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) print(l) l.append(4) print(l) # l.append('3333333') #報錯,必須爲int類型 print(l.mid) #基於受權,得到insert方法 l.insert(0,-123) print(l)
date_dic={ 'ymd':'{0.year}:{0.month}:{0.day}', 'dmy':'{0.day}/{0.month}/{0.year}', 'mdy':'{0.month}-{0.day}-{0.year}', } class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day def __format__(self, format_spec): if not format_spec or format_spec not in date_dic: 若是輸入的爲空或者輸入的格式不在規定的字典中,賦予默認格式 format_spec='ymd' fmt=date_dic[format_spec] return fmt.format(self) d1=Date(2016,12,29) print(format(d1)) #默認輸出模式 print('{:mdy1}'.format(d1)) #沒有mdy1這個模式。因此默認ymd模式輸出 print('{:mdy}'.format(d1)) #mdy輸出模式 print(format(d1,"dmy")) #dmy輸出模式 print("{:dmy}".format(d1)) #dmy輸出模式,同上
from collections import Iterable(可迭代對象),Iterator(迭代器) class Foo: def __init__(self,start=0,stop=0): self.start=start self.stop=stop self.con=0 def __iter__(self): return self def __next__(self): if self.con >= self.start and self.start >=self.stop : raise StopIteration if self.stop==0: if self.con < self.start: n = self.con self.con += 1 return n else: self.con=self.stop n=self.start self.start+=1 return n f=Foo(1,6) print(isinstance(f,Iterator)) #判斷f是不是迭代器 for i in Foo(1,6): print(i) for i in Foo(6): print(i)
class Foo: '我是描述信息' pass print(Foo.__doc__) 輸出:'我是描述信息'
這個屬性沒法被繼承接口
class Open: def __init__(self,name,mode="w"): self.x=open(name,mode=mode) self.name=name self.mode=mode def __enter__(self): #出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量 return self def __exit__(self, exc_type, exc_val, exc_tb): #with中代碼塊執行完畢後執行 print("exc_type",exc_type) #捕獲異常。獲取異常的各個信息。 print("exc_val",exc_val) print("exc_tb",exc_tb) return True def write(self,value): self.x.write(value) def __getattr__(self, item): return getattr(self.x,item) # a=Open("a.txt","r") # print(a.read()) # a.seek(0) # print(a.read()) with Open("a.txt","r") as f: print(f.read()) raise TypeError("error!") print("-------------------------")
對象後面加括號,觸發執行。
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執行 __init__ obj() # 執行 __call__
元類是類的類,是類的模板
元類是用來控制如何建立類的,正如類是建立對象的模板同樣
type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象
方式一:
1 class Foo: 2 def func(self): 3 print('from func')
方式二:
1 def func(self): 2 print('from func') 3 x=1 4 Foo=type('Foo',(object,),{'func':func,'x':1})
class B(type): def __init__(self,name,bases=None,dict=None): print("init B") self.name=name def __call__(self, *args, **kwargs): print("call") obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class A(object,metaclass=B): def __init__(self,name): print("init A") self.name=name def __new__(cls, *args, **kwargs): print("new A") return super().__new__(cls) a=A("name1") print(a.__dict__)
class Mytype(type): def __init__(self,what,bases=None,dict=None): print(what,bases,dict) def __call__(self, *args, **kwargs): print('--->') obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Room(metaclass=Mytype): def __init__(self,name): self.name=name r1=Room('alex') print(r1.__dict__)
class Mymeta(type): def __init__(self,name,bases,dic): print('===>Mymeta.__init__') def __new__(cls, *args, **kwargs): print('===>Mymeta.__new__') return type.__new__(cls,*args,**kwargs) def __call__(self, *args, **kwargs): print('aaa') obj=self.__new__(self) self.__init__(self,*args,**kwargs) return obj class Foo(object,metaclass=Mymeta): def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): return object.__new__(cls) ''' 須要記住一點:名字加括號的本質(即,任何name()的形式),都是先找到name的爹,而後執行:爹.__call__ 而爹.__call__通常作兩件事: 1.調用name.__new__方法並返回一個對象 2.進而調用name.__init__方法對兒子name進行初始化 ''' ''' class 定義Foo,並指定元類爲Mymeta,這就至關於要用Mymeta建立一個新的對象Foo,因而至關於執行 Foo=Mymeta('foo',(...),{...}) 所以咱們能夠看到,只定義class就會有以下執行效果 ===>Mymeta.__new__ ===>Mymeta.__init__ 實際上class Foo(metaclass=Mymeta)是觸發了Foo=Mymeta('Foo',(...),{...})操做, 遇到了名字加括號的形式,即Mymeta(...),因而就去找Mymeta的爹type,而後執行type.__call__(...)方法 因而觸發Mymeta.__new__方法獲得一個具體的對象,而後觸發Mymeta.__init__方法對對象進行初始化 ''' ''' obj=Foo('egon') 的原理同上 ''' ''' 總結:元類的難點在於執行順序很繞,其實咱們只須要記住兩點就能夠了 1.誰後面跟括號,就從誰的爹中找__call__方法執行 type->Mymeta->Foo->obj Mymeta()觸發type.__call__ Foo()觸發Mymeta.__call__ obj()觸發Foo.__call__ 2.__call__內按前後順序依次調用兒子的__new__和__init__方法 '''