面向对象之反射、包装、(定制)
什么是反射?
反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省),
這一概念的提出很快引發了計算機科學領域關于應用反射的研究。它首次被程序語言的設計領域所采用。并在Lisp和面向
對象方面取得了成績。
?
一、python3中四個可以實現自省的函數,下列方法適用于類和對象
class BlackMedium:feture='Ugly'def __init__(self,name,addr):self.name=nameself.addr=addrdef sell_hourse(self):print('[%s]正在賣房子,傻逼才買呢'%self.name)def rent_hourse(self):print('[%s]正在租房子,傻逼才買呢'%self.name)b1=BlackMedium('黑中介','殯儀館')?
1、hasattr(object,name)
判斷object中有沒有一個name字符串對應的方法或屬性
class BlackMedium:feture='Ugly'def __init__(self,name,addr):self.name=nameself.addr=addrdef sell_hourse(self):print('[%s]正在賣房子,傻逼才買呢'%self.name)def rent_hourse(self):print('[%s]正在租房子,傻逼才買呢'%self.name)b1=BlackMedium('黑中介','殯儀館') print(b1.__dict__)#{'name': '黑中介', 'addr': '殯儀館'} # b1.name------->b1.__dict__['name] print(b1.name)#黑中介 print(b1.addr)#殯儀館 #用hasattr檢測類的屬性和方法,存在返回TRUE,不存在返回FALSE print(hasattr(b1,'name'))#True print(hasattr(b1,'addr'))#True print(hasattr(b1,'namnmae'))#False hasattr(object,name)?
2、getattr(object,name,default=None)
檢測正確時,有屬性就返回屬性值,有方法就返回方法的內存地址,加()就可以運行
檢測有錯誤時,不給default賦值會報錯,報錯時寫什么會提示什么
#檢測屬性,屬性存在就會打印值,屬性不存在會打印報錯或default print(getattr(b1,'name'))#黑中介 print(getattr(b1,'aaaaaa'))#有報錯提示 print(getattr(b1,'aaaaaa','沒有此屬性'))#有報錯提示:沒有此屬性#檢測方法,有則返回內存地址,加()就可以運行,沒有則報錯或提示default信息 print(getattr(b1,'sell_hourse','無此方法'))#返回sell_hourse方法的內存地址,-------》b1.sell_hourse #<bound method BlackMedium.sell_hourse of <__main__.BlackMedium object at 0x0000020EE5447198>> #有內存地址加()就可以運行 func=getattr(b1,'sell_hourse') func()#[黑中介]正在賣房子,傻逼才買呢print(getattr(b1,'jskdkflsd','無此方法'))#無此方法 getattr(object,name,default)?
3、setattr(x,y,z)
可以增加修改屬性,也可以增加方法,
x傳入對象,y傳入字符串相當于字典中的key,x傳入值相當于字典中的value
print(b1.__dict__)#{'name': '黑中介', 'addr': '殯儀館'} setattr(b1,'name','SB')#修改屬性 setattr(b1,'sb',True)#增加屬性 print(b1.__dict__)#{'name': 'SB', 'addr': '殯儀館', 'sb': True} setattr(b1,'func',lambda self:self.name+'SB')#增加函數屬性 # print(b1.func(b1))#黑中介SB setattr(x,y,z)?
4、delattr(x,y)
刪除屬性x,y同上
print(b1.__dict__)#{'name': '黑中介', 'addr': '殯儀館'} delattr(b1,'name')#刪除屬性------》del b1.name # del b1.name print(b1.__dict__)#{'addr': '殯儀館'} setattr(x,y)?
5、動態模塊導入
1、把模塊名或文件名以字符串形式傳給__import__,如果模塊在二級文件內,__import__方法只能拿到最頂級模塊 module_t=__import__('三級菜單')#執行module_t就會執行模塊內的代碼 print(module_t)2、 import imaplib m=imaplib.import_module('文件.三級菜單')#直接定位到三級菜單 print(m) 3、導入有*號時,如果模塊里面的屬性有 _名字 的屬性或方法時, 帶 下劃線的屬性或方法則不能被導入 from 模塊名 import *?補充:
一切皆對象,文件也是對象同樣可以使用自省的方法
1、情景:當你寫的代碼有上萬行時(此時已是大佬 ^_^),想不起來某個功能是否完成既可以使用下面的方法來判斷
x=111 y=222 #用sys可以導入模塊自己,用hasattr可以檢測功能是否完成 import sys obj=sys.modules[__name__] print(hasattr(obj,'x'))#True print(hasattr(obj,'xsdf'))#False 導入模塊自己?
2、情景:做程序開發,每個人寫不同的功能,當需要用到別人的功能時不知道是否完成,因此我們可以導入同事寫的代碼文件,用hasattr判斷你需要的功能是否完成
import test as obj print(obj)print(hasattr(obj,'say_hi'))if hasattr(obj,'say_hi'):func=getattr(obj,'say_hi')func()else:print('其他的邏輯') 導入別人的模塊?
二、下劃線開頭的三種attr方法
這三種方法是給實例用的和類沒關系
1、__getattr__(self,item)
只有使用點調用屬性且屬性不存在時才會觸發__getattr__
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef __getattr__(self, item):print('執行__getattr__,item是 %s'%item)#調用不存在屬性時觸發__getattr__ f1=Foo('飛樂',18) print(f1.__dict__)#{'name': '飛樂', 'age': 18} f1.name#不會觸發__getattr__ f1.jsdlkf#執行__getattr__,item是 jsdlkf __getattr__(self,item)?
2、__delattr__(self,item)
刪除屬性時會觸發
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef __delattr__(self, item):#只要有刪除操作就會觸發__delattr__,并不一定能刪除,# 下面的內容會講到觸發__delattr__并且刪除值print('執行__delattr__,item是 %s'%item) f1=Foo('飛樂',18) print(f1.__dict__)#{'name': '飛樂', 'age': 18} del f1.age#執行__delattr__,item是 age print(f1.__dict__)#{'name': '飛樂', 'age': 18} __delattr__(self,item)?
3、__setattr__(self,key,value)
增加或修改屬性會觸發__setattr__的執行,在實例化對象時__init__函數屬于增加屬性操作,也會觸發__setattr__的執行?
class Foo:def __init__(self,name,age):#會觸發__setattr__self.name=nameself.age=agedef __setattr__(self, key, value):#添加或修改屬性會觸發它的執行print('執行__setattr__,key: %s value: %s'%(key,value))# self.kye=value #一直觸發__setattr__無期遞歸,你好好想想self.__dict__[key]=value#應該使用它 f1=Foo('飛樂',18) #執行__setattr__,key: name value: 飛樂 #執行__setattr__,key: age value: 18 print(f1.__dict__)#{'name': '飛樂', 'age': 18} f1.sex='male'#增加屬性,執行__setattr__,key: sex value: maleprint(f1.name)#飛樂 f1.name='king'#修改屬性,執行__setattr__,key: name value: king print(f1.__dict__)#{'name': 'king', 'age': 18, 'sex': 'male'} print(f1.name)#king __setattr__(self,key,value)?
4、利用attr方法定制屬于自己的方法
根據下方代碼可以自己擴展,好好想想
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef __getattr__(self, item):print('%s 屬性不存在'%item)def __setattr__(self, key, value):print('正在設置--------》')if type(value) is str:# self.key=value 會觸發__setattr__遞歸self.__dict__[key]=valueelse:print('屬性添加必須是字符串')def __delattr__(self, item):print('不允許刪除屬性 【%s】'%item)#del self.__dict__[item]#self.__dict__.pop(item) # f1=Foo('飛樂',18)#屬性添加必須是字符串 f1=Foo('飛樂','18')#增加或修改屬性會觸發__setattr__ #正在設置--------》 #正在設置--------》 f1.name#不會觸發__getattr__ f1.king#觸發__getattr__ king 屬性不存在 print(f1.__dict__)#{'name': '飛樂', 'age': '18'} f1.name='king'#觸發了__setattr__ print(f1.__dict__)# {'name': 'king', 'age': '18'} del f1.name#不允許刪除屬性 【name】 定制?
三、包裝
包裝標準類型,通過繼承和派生進行包裝定制屬于自己的數據類型
1、通過繼承派生包裝
class List(list):def append(self, object):if type(object) is str:super().append(object)#繼承父類的append方法else:print('類型必須是字符串')def show_mid(self):mid_index=int(len(self)/2)return self[mid_index]l1=List('helloword') print(l1,type(l1))#['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'd'] <class '__main__.List'> l1.append('king') print(l1)#['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'd', 'king'] l1.append(3245)#類型必須是字符串類型 print(l1.show_mid())#w 定制屬于自己的list方法?
2、通過__getattr__授權(包裝)
授權也是一種包裝用__getattr__方法,不用繼承和派生的方法來實現
import time class Open:def __init__(self,filename,mode='r',encoding='utf-8'):#用類有兩種方法一種是繼承另一種是組合self.file=open(filename,mode,encoding=encoding)#組合self.mode=modeself.encoding=encodingdef write(self,line):t=time.strftime('%Y-%m-%d %X')self.file.write('%s %s'%(t,line))def __getattr__(self, item):return getattr(self.file,item)#返回item方法的內存地址, #f1的屬性找不到時會觸發__getattr__的執行 f1=Open('a.txt','w+') print(f1.write)#返回的內存地址加()就可以執行 #<built-in method write of _io.TextIOWrapper object at 0x000002138D1E8B40> f1.write('helloword')#把內容寫入到文件中 f1.seek(0) print(f1.read())#helloword#f1的write能找到時 f1.write('CPU過載過高\n') f1.write('內存不足\n') f1.write('系統被不明病毒攻擊\n') f1.seek(0) print(f1.read())#讀出了定制寫入的內容 # 2018-09-10 22:34:34 helloword2018-09-10 22:34:34 CPU過載過高 # 2018-09-10 22:34:34 內存不足 # 2018-09-10 22:34:34 系統被不明病毒攻擊 授權?
轉載于:https://www.cnblogs.com/happyfei/p/9618996.html
總結
以上是生活随笔為你收集整理的面向对象之反射、包装、(定制)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在idea里如何实现Git项目回滚
- 下一篇: 萌萌机器人布娃娃图片_萌萌机器人教程