十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
?
成都創(chuàng)新互聯(lián)公司長期為上1000家客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為新華企業(yè)提供專業(yè)的成都網(wǎng)站設計、成都網(wǎng)站制作,新華網(wǎng)站改版等技術服務。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
目錄
reflection相關的內(nèi)建函數(shù):...1
反射相關的魔術方法(__getattr__()、__setattr__()、__delattr__()):...7
反射相關的魔術方法(__getattribute__):...9
?
?
?
reflection反射
?
指運行時,獲取類型定義信息;運行時通過實例能查出實例及所屬類的類型相關信息;
一個對象能夠在運行時,像照鏡子一樣,反射出它的所有類型信息;
簡單說,在python中,能通過一個對象,找出其type、class、attribute、method的能力,稱為反射或自?。?/p>
具有反射能力的函數(shù)有:type()、isinstance()、callable()、dir()、getattr();
?
注:
運行時,區(qū)別于編譯時,指程序被加載到內(nèi)存中執(zhí)行的時候;
?
getattr(object,name[,default]),通過name返回object的屬性值,當屬性不存在,將使用default返回,如果沒有default,則拋AttributeError,name必須為字符串;
setattr(object,name,value),object(實例或類)屬性存在則覆蓋,不存在新增;
hasattr(object,name),判斷對象是否有這個名字的屬性,name必須為字符串;
?
動態(tài)添加屬性方式,與裝飾器修改一個類、mixin方式的差異?
動態(tài)添加屬性,是運行時改變類或者實例的方式,因此反射能力具有更大的靈活性;
裝飾器和mixin,在定義時就決定了;
?
注:
一般運行時動態(tài)增,很少刪;
self.__class__,同type(self);即實例a.__class__,同type(a);
?
例:
class Point:
??? def __init__(self,x,y):
??????? self.x = x
??????? self.y = y
?
??? def __str__(self):
??????? return 'Point({},{})'.format(self.x,self.y)
?
??? __repr__ = __str__
?
??? def show(self):
??????? print(self.x,self.y)
?
p = Point(4,5)
print(p.__dict__)
p.__dict__['y'] = 16
print(p.__dict__['y'])?? #通過實例的屬性字典__dict__訪問實例的屬性,本質(zhì)上是利用反射的能力,這種訪問方式不優(yōu)雅,python提供了相關的內(nèi)置函數(shù)
p.z = 10
print(p.__dict__)
?
p1 = Point(4,5)
p2 = Point(10,10)
print(repr(p1),repr(p2))
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',18)
print(getattr(p1,'__dict__'))
?
if hasattr(p1,'show'):?? #動態(tài)調(diào)用方法
??? getattr(p1,'show')()
?
if not hasattr(Point,'add'):?? #源碼中常用此方式
??? setattr(Point,'add',lambda self,other: Point(self.x + other.x,self.y + other.y))
?
print(Point.add)
print(p1.add)
print(p1.add(p2))
輸出:
{'x': 4, 'y': 5}
16
{'x': 4, 'y': 16, 'z': 10}
Point(4,5) Point(10,10)
{'x': 4, 'y': 5}
{'x': 4, 'y': 16, 'z': 18}
4 16
Point(14,26)
?
例:
class A:
??? def __init__(self,x):
??????? self.x = x
?
a = A(5)
setattr(A,'y',10)?? #運行時改變屬性,在類上操作
print(A.__dict__)
print(a.__dict__)
print(getattr(a,'x'))
print(getattr(a,'y'))?? #實例沒有y,向上找自己類的
# print(getattr(a,'z'))?? #X
print(getattr(a,'z',100))
setattr(a,'y',1000)?? #在實例上操作
print(A.__dict__)
print(a.__dict__)
?
# setattr(a,'mtd',lambda self: 1)?? #在實例上定義方法,看似可以,實際不行,未綁定self,若要在調(diào)用時不出錯,需把實際名寫上,如a.mtd(a)
# a.mtd()?? #X
# print(a.mtd(a))?? #V
print(a.__dict__)
?
setattr(A,'mtd',lambda self: 2)?? #在類上定義方法沒問題
print(a.mtd())
print(A.__dict__)
輸出:
{'__module__': '__main__', '__init__':
{'x': 5}
5
10
100
{'__module__': '__main__', '__init__':
{'x': 5, 'y': 1000}
{'x': 5}
2
{'__module__': '__main__', '__init__':
?
習題:
命令分發(fā)器,通過名稱找對應的函數(shù)執(zhí)行;
思路:名稱找對象的方法;
?
函數(shù)方式實現(xiàn):
方1:
def dispatcher():
??? cmds = {}
?
??? def reg(cmd,fn):
??????? if isinstance(cmd,str):
??????????? cmds[cmd] = fn
??????? else:
??????????? print('error')
?
??? def run():
???? ???while True:
??????????? cmd = input('plz input command: ')
??????????? if cmd.strip() == 'quit':
??????????????? return
??????????? print(cmds.get(cmd.strip(),defaultfn)())
?
??? def defaultfn():
??????? return 'default function'
?
??? return reg,run
?
reg,run = dispatcher()
?
reg('cmd1',lambda : 1)
reg('cmd2',lambda : 2)
?
run()
輸出:
plz input command: cmd3
default function
plz input command:? cmd2
2
plz input command: cmd1
1
plz input command: quit
?
方2:
def cmds_dispatcher():
??? cmds = {}
?
??? def reg(name):
??????? def wrapper(fn):
??????????? cmds[name] = fn
??????????? return fn
??????? return wrapper
?
??? def dispatcher():
??????? while True:
??????????? cmd = input('plz input comd: ')
??????????? if cmd.strip() == 'quit':
??????????????? return
??????????? print(cmds.get(cmd.strip(),defaultfn)())
?
??? def defaultfn():
??????? return 'default function'
?
??? return reg,dispatcher
?
reg,dispatcher = cmds_dispatcher()
?
@reg('cmd1')
def foo1():
??? return 1
?
@reg('cmd2')
def foo2():
??? return 2
?
dispatcher()
?
面向?qū)ο蠓绞綄崿F(xiàn):
使用setattr()和getattr()找到對象的屬性(實際是在類上加的,實例找不到逐級往上找),比自己維護一個dict來建立名稱和函數(shù)之間的關系要好;
實現(xiàn)1:
class Dispatcher:
??? def cmd1(self):
??????? return 1
?
??? def reg(self,cmd,fn):
??????? if isinstance(cmd,str):
??????????? setattr(self.__class__,cmd,fn)?? #放在類上最方便,self.__class__同type(self);不要在實例上定義,如果在實例上,setattr(self,cmd,fn),調(diào)用時要注意dis.reg('cmd2',lambda : 2)
??????? else:
??????????? print('error')
?
??? def run(self):
??????? while True:
??????????? cmd = input('plz input cmd: ')
??????????? if cmd.strip() == 'quit':
??????????????? return
??????????? print(getattr(self,cmd.strip(),self.defaultfn)())
?
??? def defaultfn(self):
??????? return 'default function'
?
dis = Dispatcher()
dis.reg('cmd2',lambda self: 2)
dis.run()
# print(dis.__class__.__dict__)
# print(dis.__dict__)
輸出:
plz input cmd: cmd1
1
plz input cmd: cmd2
2
plz input cmd: cmd3
default function
plz input cmd: 11
default function
?
實現(xiàn)2:
class Dispatcher:
??? def __init__(self):
??????? self._run()
?
??? def cmd1(self):
??????? return 1
?
??? def cmd2(self):
??????? return 2
?
??? def reg(self,cmd,fn):
??????? if isinstance(cmd,str):
??????????? setattr(self.__class__,cmd,fn)
??????? else:
??????????? print('error')
?
??? def _run(self):
??????? while True:
??????????? cmd = input('plz input cmd: ')
??????????? if cmd.strip() == 'quit':
??????????????? return
??????????? print(getattr(self,cmd.strip(),self.defaultfn)())
?
??? def defaultfn(self):
??????? return 'default function'
?
Dispatcher()
輸出:
plz input cmd: cmd1
1
plz input cmd: cmd2
2
plz input cmd: cmd3
default function
plz input cmd: abcd
default function
?
?
?
__getattr__(),當在實例、實例的類及祖先類中查不到屬性,才調(diào)用此方法;
__setattr__(),通過點訪問屬性,進行增加、修改都要調(diào)用此方法;
__delattr__(),當通過實例來刪除屬性時調(diào)用此方法,刪自己有的屬性;
?
一個類的屬性會按照繼承關系找,如果找不到,就是執(zhí)行__getattr__(),如果沒有這個方法,拋AttributeError;
查找屬性順序為:
instance.__dict__-->instance.__class__.__dict__-->繼承的祖先類直到object的__dict__-->調(diào)用__getattr__(),如果沒有__getattr__()則拋AttributeError異常;
?
__setattr__()和__delattr__(),只要有相應的操作(如初始化時self.x = x或運行時a.y = 200觸發(fā)__setattr__(),del a.m則觸發(fā)__delattr__())就會觸發(fā),做攔截用,攔截做增加或修改,屬性要加到__dict__中,要自己完成;
?
這三個魔術方法的第一個參數(shù)為self,則如果用類名.屬性操作時,則這三個魔術方法管不著;
?
例:
class A:
??? m = 6
??? def __init__(self,x):
??????? self.x = x
?
??? def __getattr__(self, item):?? #對象的屬性按搜索順序逐級找,找到祖先類object上也沒有對應屬性,則最后找__getattr__(),如有定義__getattr__()返回該函數(shù)返回值,如果沒有此方法,則報錯AttributeError: 'A' object has no attribute 'y'
??????? print('__getattr__',item)
?
print(A(10).x)
print(A(8).y)
輸出:
10
__getattr__ y
None
?
例:
class A:
??? m = 6
??? def __init__(self,x):
??????? self.x = x?? #__init__()中的self.x = x也調(diào)用__setattr__()
?
??? def __getattr__(self, item):
??????? print('__getattr__',item)
??????? # self.__dict__[item] = 'default_value'
?
??? def __setattr__(self, key, value):
??????? print('__setattr__',key,value)
??????? # self.__dict__[key] = value
?
??? def __delattr__(self, item):
??????? print('__delattr__')
?
a = A(8)?? #初始化時的self.x = x也調(diào)用__setattr__()
a.x?? #調(diào)用__getattr__()
a.x = 100 ??#調(diào)用__setattr__(),實例的__dict__為空沒有x屬性,雖有觸發(fā)__setattr__(),但沒寫到__dict__中,要自己寫
a.x
a.y
a.y = 200
a.y
print(a.__dict__)?? #__getattr__()和__setattr__()都有,實例的__dict__為空,用a.x訪問屬性時按順序都沒找到最終調(diào)用__getattr__()
print(A.__dict__)
?
del a.m
輸出:
__setattr__ x 8
__getattr__ x
__setattr__ x 100
__getattr__ x
__getattr__ y
__setattr__ y 200
__getattr__ y
{}
{'__module__': '__main__', 'm': 6, '__init__':
__delattr__
?
?
?
?
__getattribute__(),實例所有屬性調(diào)用,都從這個方法開始;
實例的所有屬性訪問,第一個都會調(diào)用__getattribute__()方法,它阻止了屬性的查找,該方法應該返回(計算后的)值或拋AttributeError,它的return值將作為屬性查找的結(jié)果,如果拋AttributeError則直接調(diào)用__getattr__(),表示屬性沒有找到;
為了避免在該方法中無限的遞歸,它的實現(xiàn)應該永遠調(diào)用基類的同名方法以訪問需要的任何屬性,如object.__getattribute__(self,name);
除非明確知道__getattribute__()方法用來做什么,否則不要使用它,攔截面太大;
?
屬性查找順序:
實例調(diào)用__getattribute__()-->instance.__dict__-->instance.__class__.__dict__-->繼承的祖先類直到object的__dict__-->調(diào)用__getattr__();
?
例:
class A:
??? m = 6
??? def __init__(self,x):
??????? self.x = x
?
??? def __getattr__(self, item):
??????? print('__getattr__',item)
??????? # self.__dict__[item] = 'default_value'
?
??? def __setattr__(self, key, value):
??????? print('__setattr__',key,value)
??????? # self.__dict__[key] = value
?
??? def __delattr__(self, item):
??????? print('__delattr__')
?
??? def __getattribute__(self, item):
??????? print('__getattribute__',item)
??????? raise AttributeError(item)?? #如果拋AttributeError則直接調(diào)用__getattr__(),表示屬性沒找到
??????? # return self.__dict__[item]?? #遞歸調(diào)用
??????? # return object.__getattribute__(self,item) ??#繼續(xù)向后找,這樣做沒意義
?
a = A(8)
a.x
a.y
a.z
輸出:
__setattr__ x 8
__getattribute__ x
__getattr__ x
__getattribute__ y
__getattr__ y
__getattribute__ z
__getattr__ z
?