6.面向對象:反射,雙下方法

  • 2019 年 10 月 3 日
  • 筆記

一. 反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和面向對象方面取得了成績。

python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)

四個可以實現自省的函數

下列方法適用於類和對象(一切皆對象,類本身也是一個對象)

class Foo:      f = '類的靜態變量'      def __init__(self,name,age):          self.name=name          self.age=age        def 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__)    對實例化對象的示例

對對象的反射

class Foo(object):        staticField = "old boy"        def __init__(self):          self.name = 'wupeiqi'        def func(self):          return 'func'        @staticmethod      def bar():          return 'bar'    print getattr(Foo, 'staticField')  print getattr(Foo, 'func')  print getattr(Foo, 'bar')

對類的反射

import sys      def 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.py      index.py    當前文件:      index.py  """  # 另一個模塊中的代碼  import module_test as obj    #obj.test()    print(hasattr(obj,'test'))    getattr(obj,'test')()    其他模塊的示例

其他模塊的反射

反射的應用:

了解了反射的四個函數。那麼反射到底有什麼用呢?它的應用場景是什麼呢?

現在讓我們打開瀏覽器,訪問一個網站,你單擊登錄就跳轉到登錄界面,你單擊註冊就跳轉到註冊界面,等等,其實你單擊的其實是一個個的鏈接,每一個鏈接都會有一個函數或者方法來處理。

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():      pass    print(func)  # <function func at 0x00000260A2E690D0>      class A:      def func(self):          pass    print(A.func)  # <function A.func at 0x0000026E65AE9C80>  obj = A()  print(obj.func)  # <bound method A.func of <__main__.A object at 0x00000230BAD4C9E8>>

View Code

2.2 通過types模塊驗證

from types import FunctionType  from types import MethodType    def func():      pass      class A:      def func(self):          pass    obj = A()      print(isinstance(func,FunctionType))  # True  print(isinstance(A.func,FunctionType))  # True  print(isinstance(obj.func,FunctionType))  # False  print(isinstance(obj.func,MethodType))  # True

View Code

2.3 靜態方法是函數

from types import FunctionType  from types import MethodType    class A:        def func(self):          pass        @classmethod      def func1(self):          pass        @staticmethod      def func2(self):          pass  obj = A()    # 靜態方法其實是函數  # print(isinstance(A.func2,FunctionType))  # True  # print(isinstance(obj.func2,FunctionType))  # True

View Code

2.4 函數與方法的區別

那麼,函數和方法除了上述的不同之處,我們還總結了一下幾點區別。

(1)函數的是顯式傳遞數據的。如我們要指明為len()函數傳遞一些要處理數據。

(2)函數則跟對象無關。

(3)方法中的數據則是隱式傳遞的。

(4)方法可以操作類內部的數據。

(5)方法跟對象是關聯的。如我們在用strip()方法是,是不是都是要通過str對象調用,比如我們有字符串s,然後s.strip()這樣調用。是的,strip()方法屬於str對象。

我們或許在日常中會口語化稱呼函數和方法時不嚴謹,但是我們心中要知道二者之間的區別。

在其他語言中,如Java中只有方法,C中只有函數,C++么,則取決於是否在類中。

三. 雙下方法

定義:雙下方法是特殊方法,他是解釋器提供的 由爽下劃線加方法名加雙下劃線 __方法名__的具有特殊意義的方法,雙下方法主要是python源碼程序員使用的,我們在開發中盡量不要使用雙下方法,但是深入研究雙下方法,更有益於我們閱讀源碼。

調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:init

3.01 __len

class B:      def __len__(self):          print(666)    b = B()  len(b) # len 一個對象就會觸發 __len__方法。    class A:      def __init__(self):          self.a = 1          self.b = 2        def __len__(self):          return len(self.__dict__)  a = A()  print(len(a))

View Code

3.02 __hash

class A:      def __init__(self):          self.a = 1          self.b = 2        def __hash__(self):          return hash(str(self.a)+str(self.b))  a = A()  print(hash(a))

View Code

3.03 str

如果一個類中定義了__str__方法,那麼在打印 對象 時,默認輸出該方法的返回值。

class A:      def __init__(self):          pass      def __str__(self):          return '太白'  a = A()  print(a)  print('%s' % a)

View Code

3.04 repr

如果一個類中定義了__repr__方法,那麼在repr(對象) 時,默認輸出該方法的返回值。

class A:      def __init__(self):          pass      def __repr__(self):          return '太白'  a = A()  print(repr(a))  print('%r'%a)

View Code

3.05 call

對象後面加括號,觸發執行。

註:構造方法__new__的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:        def __init__(self):          pass        def __call__(self, *args, **kwargs):            print('__call__')      obj = Foo() # 執行 __init__  obj()       # 執行 __call__

View Code

3.06 __eq

class A:      def __init__(self):          self.a = 1          self.b = 2        def __eq__(self,obj):          if  self.a == obj.a and self.b == obj.b:              return True  a = A()  b = A()  print(a == b)

View Code

3.07 del

析構方法,當對象在內存中被釋放時,自動觸發執行。

註:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

3.08__new

class A:      def __init__(self):          self.x = 1          print('in init function')      def __new__(cls, *args, **kwargs):          print('in new function')          return object.__new__(A, *args, **kwargs)    a = A()  print(a.x)

View Code

class A:      __instance = None      def __new__(cls, *args, **kwargs):          if cls.__instance is None:              obj = object.__new__(cls)              cls.__instance = obj          return cls.__instance  

單例模式

單例模式具體分析

單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
【採用單例模式動機、原因】
對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重複對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味着在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
【單例模式優缺點】
【優點】
一、實例控制
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
二、靈活性
因為類控制了實例化過程,所以類可以靈活更改實例化過程。
【缺點】
一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用

3.09 __item__系列

class Foo:      def __init__(self,name):          self.name=name        def __getitem__(self, item):          print(self.__dict__[item])        def __setitem__(self, key, value):          self.__dict__[key]=value      def __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__)  

View Code

3.10 上下文管理器相關

enter __exit

# 如果想要對一個類的對象進行with  as 的操作 不行。  class A:      def __init__(self, text):          self.text = text    with A('大爺') as f1:      print(f1.text)  

沒他們不可以這樣操作

class A:        def __init__(self, text):          self.text = text        def __enter__(self):  # 開啟上下文管理器對象時觸發此方法          self.text = self.text + '您來啦'          return self  # 將實例化的對象返回f1        def __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 = name          self.mode = mode        def __enter__(self):          print "Hi enter here!!"          self.filehander = open(self.name,self.mode)          return self.filehander        def __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 = num        def 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:      pass      class Department:      pass      class StarkConfig:      def __init__(self, num):          self.num = num        def 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      site = AdminSite()  site.register(UserInfo, StarkConfig)  # 1  # obj = site._registry[UserInfo]()    # 2  obj = site._registry[UserInfo](100)  obj.run()    -------  class UserInfo:      pass    class Department:      pass    class StarkConfig:      def __init__(self,num):          self.num = num        def 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_display    s1 = 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_display      s1 = A()  print(s1.get_list())    ------  class A:      list_display = []        def get_list(self):          self.list_display.insert(0,33)          return self.list_display    class B(A):      list_display = [11,22]      s1 = A()  s2 = B()  print(s1.get_list())  print(s2.get_list())  

View Code