Python面向对象反射,双下方法
一. 反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了計算機科學領域關于應用反射性的研究。它首先被程序語言的設計領域所采用,并在Lisp和面向對象方面取得了成績。
python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數
下列方法適用于類和對象(一切皆對象,類本身也是一個對象)
對實例化對象的示例
class Foo:f = '類的靜態變量'def __init__(self,name,age):self.name=nameself.age=agedef say_hi(self):print('hi,%s'%self.name)obj=Foo('egon',73)#檢測是否含有某屬性 print(hasattr(obj,'name')) print(hasattr(obj,'say_hi'))#獲取屬性 n=getattr(obj,'name') print(n) func=getattr(obj,'say_hi') func()print(getattr(obj,'aaaaaaaa','不存在啊')) #報錯#設置屬性 setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj))#刪除屬性 delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_name111')#不存在,則報錯print(obj.__dict__)對對象的反射
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo(object):staticField = "old boy"def __init__(self):self.name = 'wupeiqi'def func(self):return 'func'@staticmethoddef bar():return 'bar'print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar')對類的反射
import sysdef s1():print 's1'def s2():print 's2'this_module = sys.modules[__name__]hasattr(this_module, 's1') getattr(this_module, 's2')當前模塊的反射
#一個模塊中的代碼 def test():print('from the test') """ 程序目錄:module_test.pyindex.py當前文件:index.py """ # 另一個模塊中的代碼 import module_test as obj#obj.test()print(hasattr(obj,'test'))getattr(obj,'test')()其他模塊的反射
反射的應用:
了解了反射的四個函數。那么反射到底有什么用呢?它的應用場景是什么呢?
現在讓我們打開瀏覽器,訪問一個網站,你單擊登錄就跳轉到登錄界面,你單擊注冊就跳轉到注冊界面,等等,其實你單擊的其實是一個個的鏈接,每一個鏈接都會有一個函數或者方法來處理。
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class User:def login(self):print('歡迎來到登錄頁面')def register(self):print('歡迎來到注冊頁面')def save(self):print('歡迎來到存儲頁面')while 1:choose = input('>>>').strip()if choose == 'login':obj = User()obj.login()elif choose == 'register':obj = User()obj.register()elif choose == 'save':obj = User()obj.save()沒學反射之前的解決方式
class User:def login(self):print('歡迎來到登錄頁面')def register(self):print('歡迎來到注冊頁面')def save(self):print('歡迎來到存儲頁面')user = User() while 1:choose = input('>>>').strip()if hasattr(user,choose):func = getattr(user,choose)func()else:print('輸入錯誤。。。。')學了反射之后解決方式
有多簡單,一目了然。
二. 函數 vs 方法
學到這里,我終于能回答你一直以來可能有的一個疑問。那就是,之前的學習中我們稱len()為函數(口誤時稱為方法)卻稱如str的strip為方法,那它到底叫什么?函數和方法有什么區別和相同之處?我在這里就正式的解釋一下。
2.1 通過打印函數(方法)名確定
def func():passprint(func) # <function func at 0x00000260A2E690D0>class A:def func(self):passprint(A.func) # <function A.func at 0x0000026E65AE9C80> obj = A() print(obj.func) # <bound method A.func of <__main__.A object at 0x00000230BAD4C9E8>> View Code2.2 通過types模塊驗證
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from types import FunctionType from types import MethodTypedef func():passclass A:def func(self):passobj = A()print(isinstance(func,FunctionType)) # True print(isinstance(A.func,FunctionType)) # True print(isinstance(obj.func,FunctionType)) # False print(isinstance(obj.func,MethodType)) # True2.3 靜態方法是函數
from types import FunctionType from types import MethodTypeclass A:def func(self):pass@classmethoddef func1(self):pass@staticmethoddef func2(self):pass obj = A()# 靜態方法其實是函數 # print(isinstance(A.func2,FunctionType)) # True # print(isinstance(obj.func2,FunctionType)) # True2.4 函數與方法的區別
那么,函數和方法除了上述的不同之處,我們還總結了一下幾點區別。
(1)函數的是顯式傳遞數據的。如我們要指明為len()函數傳遞一些要處理數據。
(2)函數則跟對象無關。
(3)方法中的數據則是隱式傳遞的。
(4)方法可以操作類內部的數據。
(5)方法跟對象是關聯的。如我們在用strip()方法是,是不是都是要通過str對象調用,比如我們有字符串s,然后s.strip()這樣調用。是的,strip()方法屬于str對象。
我們或許在日常中會口語化稱呼函數和方法時不嚴謹,但是我們心中要知道二者之間的區別。
在其他語言中,如Java中只有方法,C中只有函數,C++么,則取決于是否在類中。
三. 雙下方法
定義:雙下方法是特殊方法,他是解釋器提供的 由爽下劃線加方法名加雙下劃線 __方法名__的具有特殊意義的方法,雙下方法主要是python源碼程序員使用的,我們在開發中盡量不要使用雙下方法,但是深入研究雙下方法,更有益于我們閱讀源碼。
調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:init
3.01 __len
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class B:def __len__(self):print(666)b = B() len(b) # len 一個對象就會觸發 __len__方法。class A:def __init__(self):self.a = 1self.b = 2def __len__(self):return len(self.__dict__) a = A() print(len(a))3.02 __hash
class A:def __init__(self):self.a = 1self.b = 2def __hash__(self):return hash(str(self.a)+str(self.b)) a = A() print(hash(a))3.03 str
如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值。
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:def __init__(self):passdef __str__(self):return '太白' a = A() print(a) print('%s' % a)3.04 repr
如果一個類中定義了__repr__方法,那么在repr(對象) 時,默認輸出該方法的返回值。
class A:def __init__(self):passdef __repr__(self):return '太白' a = A() print(repr(a)) print('%r'%a)3.05 call
對象后面加括號,觸發執行。
注:構造方法__new__的執行是由創建對象觸發的,即:對象 = 類名() ;而對于 call 方法的執行是由對象后加括號觸發的,即:對象() 或者 類()()
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __init__(self):pass def __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 執行 __init__ obj() # 執行 __call__3.06 __eq
class A:def __init__(self):self.a = 1self.b = 2def __eq__(self,obj):if self.a == obj.a and self.b == obj.b:return True a = A() b = A() print(a == b)3.07 del
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
3.08__new
class A:def __init__(self):self.x = 1print('in init function')def __new__(cls, *args, **kwargs):print('in new function')return object.__new__(A, *args, **kwargs)a = A() print(a.x) ''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class A:__instance = Nonedef __new__(cls, *args, **kwargs):if cls.__instance is None:obj = object.__new__(cls)cls.__instance = objreturn cls.__instance單例模式
單例模式具體分析
單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易于外界訪問,從而方便對實例個數的控制并節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
【采用單例模式動機、原因】
對于系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重復對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味著在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例并且這個實例易于被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,并且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
【單例模式優缺點】
【優點】
一、實例控制
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
二、靈活性
因為類控制了實例化過程,所以類可以靈活更改實例化過程。
【缺點】
一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基于.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用
3.09 __item__系列
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Foo:def __init__(self,name):self.name=namedef __getitem__(self, item):print(self.__dict__[item])def __setitem__(self, key, value):self.__dict__[key]=valuedef __delitem__(self, key):print('del obj[key]時,我執行')self.__dict__.pop(key)def __delattr__(self, item):print('del obj.key時,我執行')self.__dict__.pop(item)f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)3.10 上下文管理器相關
enter __exit# 如果想要對一個類的對象進行with as 的操作 不行。 class A:def __init__(self, text):self.text = textwith A('大爺') as f1:print(f1.text)沒他們不可以這樣操作
class A:def __init__(self, text):self.text = textdef __enter__(self): # 開啟上下文管理器對象時觸發此方法self.text = self.text + '您來啦'return self # 將實例化的對象返回f1def __exit__(self, exc_type, exc_val, exc_tb): # 執行完上下文管理器對象f1時觸發此方法self.text = self.text + '這就走啦'with A('大爺') as f1:print(f1.text) print(f1.text)有他們可以這樣操作
class Diycontextor:def __init__(self,name,mode):self.name = nameself.mode = modedef __enter__(self):print "Hi enter here!!"self.filehander = open(self.name,self.mode)return self.filehanderdef __exit__(self,*para):print "Hi exit here"self.filehander.close()with Diycontextor('py_ana.py','r') as f:for i in f:print i自定義文件管理器
相關面試題:
class StarkConfig:def __init__(self,num):self.num = numdef run(self):self()def __call__(self, *args, **kwargs):print(self.num)class RoleConfig(StarkConfig):def __call__(self, *args, **kwargs):print(345)def __getitem__(self, item):return self.num[item]v1 = RoleConfig('alex') v2 = StarkConfig('太白金星') # print(v1[1]) # print(v2[2]) v1.run()------- class UserInfo:passclass Department:passclass StarkConfig:def __init__(self, num):self.num = numdef changelist(self, request):print(self.num, request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self, request):print(666, self.num)class AdminSite:def __init__(self):self._registry = {}def register(self, k, v):self._registry[k] = vsite = AdminSite() site.register(UserInfo, StarkConfig) # 1 # obj = site._registry[UserInfo]()# 2 obj = site._registry[UserInfo](100) obj.run()------- class UserInfo:passclass Department:passclass StarkConfig:def __init__(self,num):self.num = numdef changelist(self,request):print(self.num,request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self,request):print(666,self.num)class AdminSite:def __init__(self):self._registry = {}def register(self,k,v):self._registry[k] = v(k)site = AdminSite() site.register(UserInfo,StarkConfig) site.register(Department,RoleConfig)for k,row in site._registry.items():row.run()------- class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displays1 = A() print(s1.get_list())------- class A:list_display = [1, 2, 3]def __init__(self):self.list_display = []def get_list(self):self.list_display.insert(0, 33)return self.list_displays1 = A() print(s1.get_list())------ class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displayclass B(A):list_display = [11,22]s1 = A() s2 = B() print(s1.get_list()) print(s2.get_list())總結
以上是生活随笔為你收集整理的Python面向对象反射,双下方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的单引号双引号和三引号
- 下一篇: Python使用lxml模块和Reque