簡學Python第七章__class面向對象高級用法與反射

Python第七章__class面向對象高級用法與反射

歡迎加入Linux_Python學習羣python

  羣號:478616847web

 

目錄:

  • Python中關於oop的經常使用術語
  • 類的特殊方法
  • 元類
  • 反射

 

1、Python中關於oop的經常使用術語

抽象/實現ide

抽象指對現實世界問題和實體的本質表現,行爲和特徵建模,創建一個相關的子集,能夠用於 繪程序結構,從而實現這種模型。抽象不只包括這種模型的數據屬性,函數

還定義了這些數據的接口。對某種抽象的實現就是對此數據及與之相關接口的現實化(realization)。現實化這個過程對於客戶 程序應當是透明並且無關的。 oop

 

封裝/接口post

封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數。經過任何客戶端直接對數據的訪問,無視接口,與封裝性都是背道而馳的,除非程序學習

員容許這些操做。做爲實現的 一部分,客戶端根本就不須要知道在封裝以後,數據屬性是如何組織的。在Python中,全部的類屬性都是公開的,但名字可能被「混url

淆」了,以阻止未經受權的訪問,但僅此而已,再沒有其餘預防措施了。這就須要在設計時,對數據提供相應的接口,以避免客戶程序經過不規範的操做來存取封裝的數spa

據屬性。設計

注意:封裝毫不是等於「把不想讓別人看到、之後可能修改的東西用private隱藏起來」真正的封裝是,通過深刻的思考,作出良好的抽象,給出「完整且最小」

的接口,並使得內部細節能夠對外透明(注意:對外透明的意思是外部調用者能夠順利的獲得本身想要的任何功能,徹底意識不到內部細節的存在)

 

合成

合成擴充了對類的 述,使得多個不一樣的類合成爲一個大的類,來解決現實問題。合成 述了 一個異常複雜的系統,好比一個類由其它類組成,更小的組件也多是

其它的類,數據屬性及行爲, 全部這些合在一塊兒,彼此是「有一個」的關係。

 

派生/繼承/繼承結構

派生描述了子類衍生出新的特性,新類保留已存類類型中全部須要的數據和行爲,但容許修改或者其它的自定義操做,都不會修改原類的定義。

繼承描述了子類屬性從祖先類繼承這樣一種方式

繼承結構表示多「代」派生,能夠述成一個「族譜」,連續的子類,與祖先類都有關係。

 

泛化/特化

基於繼承

泛化表示全部子類與其父類及祖先類有同樣的特色。

特化描述全部子類的自定義,也就是,什麼屬性讓它與其祖先類不一樣。

 

多態與多態性

多態指的是同一種事物的多種狀態:水這種事物有多種不一樣的狀態:冰,水蒸氣

多態性的概念指出了對象如何經過他們共同的屬性和動做來操做及訪問,而不需考慮他們具體的類。

冰,水蒸氣,都繼承於水,它們都有一個同名的方法就是變成雲,可是冰.變雲(),與水蒸氣.變雲()是大相徑庭的過程,雖然調用的方法都同樣

 

自省/反射

自省也稱做反射,這個性質展現了某對象是如何在運行期取得自身信息的。若是傳一個對象給你,你能夠查出它有什麼能力,這是一項強大的特性。若是

Python不支持某種形式的自省功能,dir和type內建函數,將很難正常工做。還有那些特殊屬性,像__dict__,__name__及__doc__

 

2、類的特殊方法

一、isinstance和issubclass

isinstance(obj,cls)檢查是否obj是不是類 cls 的對象

1 class Foo(object):
2     pass
3 obj = Foo()
4 print(isinstance(obj, Foo))
isinstance

issubclass(sub, super)檢查sub類是不是 super 類的派生類

1 class Foo(object):
2     pass
3 class Test(Foo):
4     pass
5 print(issubclass(Test, Foo))
issubclass

 

二、__setattr__,__delattr__,__getattr__

__setattr__只要添加和修改屬性就會觸發它的運行,能夠利用它修改和添加屬性

 1 class Foo:
 2     def __init__(self,y):
 3         self.y=y
 4     def __setattr__(self, key, value):
 5         print('我是 setattr')
 6         # self.key=value #這就無限遞歸了
 7         self.__dict__[key]=value #因此應該利用dict
 8 
 9 def func():
10     print("from aaa")
11 
12 #__setattr__添加/修改屬性會觸發它的執行
13 f1=Foo(10)   #在init構造函數中會觸發
14 print(f1.__dict__) #輸出未賦值的對象dict
15 f1.a=func #賦值
16 print(f1.__dict__)#輸出賦值完成的對象dict
17 f1.a() #執行
setattr

__delattr__刪除屬性的時候會觸發它,能夠利用它來限制哪些屬性不能被刪除

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __delattr__(self, item):
 7         print('觸發 delattr')
 8         if item == "y":return#能夠作判斷保證一些屬性不能被刪除
 9         self.__dict__.pop(item)
10 
11 #實例化
12 f1=Foo(10)
13 
14 #__delattr__刪除屬性的時候會觸發
15 f1.__dict__['a']=10#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做
16 print(f1.__dict__)
17 del f1.y
18 print(f1.__dict__)
delattr

__getattr__只有在使用點調用不存在的屬性時候纔會觸發

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __getattr__(self, item):
 7         print('觸發 getattr')
 8         if item == 'age':
 9             return 26
10         else:
11             print("你訪問的屬性不存在")
12 
13 
14 #__setattr__添加/修改屬性會觸發它的執行
15 f1=Foo(10)
16 print(f1.y)
17 
18 #__getattr__只有在使用點調用不存在的屬性時候纔會觸發
19 print(f1.age)
20 
21 print(f1.test)
getattr

 

三、__getattribute__

這個方法表示只要用點調用就會觸發,當_getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出

異常AttributeError

 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4 
 5     def __getattr__(self, item):
 6         print('執行的是我')
 7         # return self.__dict__[item]
 8     def __getattribute__(self, item):
 9         print('無論是否存在,我都會執行')
10         raise AttributeError()
11 
12 f1=Foo(10)
13 f1.x #存在觸發
14 
15 f1.test #不存在觸發
16 #當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError
getattribute

 

四、__getitem__,__setitem__,__delitem__

用於索引操做,如字典。以上分別表示獲取、設置、刪除數據

 1 class Foo(object):
 2     def __init__(self,name):
 3         self.name = name
 4     def __getitem__(self, key):
 5         print('getitem',key)
 6  
 7     def __setitem__(self, key, value):
 8         print('setitem',key,value)
 9         self.__dict__[key]=value
10 
11     def __delitem__(self, key):
12         print('delitem',key)
13         # self.__dict__.pop(key)
14  
15 obj = Foo("FFF")
16 
17 print(obj.__dict__)
18 
19 result = obj['k1']      # 獲取自動觸發執行 __getitem__
20 obj['name'] = 'test'     # 設置自動觸發執行 __setitem__
21 del obj['k1']           # 刪除自動觸發執行 __delitem__
22 
23 print(obj.__dict__)
setitem,getitem,delitem

 

五、__str____repr__

__str__,在打印對象時觸發

1 class Foo:
2     def __str__(self):
3         print("hehe")
4         return 'test'
5 
6 obj = Foo()
7 print(obj)
str

__repr__,事實上repr跟str作了一件事,惟一的區別就是,python說str輸出的是對人類友好,repr對機器友好,記不記得第一章中的字符串格式化,就提到了

  %s 字符串 (採用str()的顯示) %r 字符串 (採用repr()的顯示)

1 class Foo(object):
2     def __str__(self):
3         return "str"
4     def __repr__(self):
5         return "repr"
6 
7 obj = Foo()
8 print("%s" %obj)
9 print("%r" %obj)
repr

 

六、__slots__

在python新式類中,從object繼承下來的類有一個變量是__slots__,默認狀況下每一個類都會有一個dict,slots的做用是阻止在實例化類時爲實例分配dict,由

__slots__管,目的是節省內存。

 1 class Foo:
 2     __slots__=['name','age']
 3     
 4 f1=Foo()
 5 f1.name='test'
 6 f1.age=22
 7 print(f1.__slots__)
 8 #這句會報錯,緣由是沒有__dict__
 9 #print(f1.__dict__)
10 
11 print(Foo.__dict__)
slots

 

七、__iter__和__next__

這兩個主要是實現迭代器協議的,下面的例子中能夠看出,首先執行__iter__,而後返回iter(),iter表示若是傳遞了第二個參數,則第一個參數必須是一個可調用

的對象(如,函數)。此時,iter建立了一個迭代器對象,用for循環這個迭代器時,都會調用第一個參數。若是__next__的返回值等於第二個值,則中止,不然

返回下一個值,也能夠不傳入第二個參數,直接返回self,那麼在for循環和使用next()的時候就會一直迭代。

 1 class Foo(object):
 2     def __init__(self):
 3         self.x = ["a","b","c"]
 4         self.a =1
 5     def __iter__(self):
 6         print("__iter__")
 7         return iter(self.__next__,4)
 8 
 9     def __next__(self):
10         print("__next__")
11         self.a+=1
12         return self.a
13 
14 itr = Foo()
15 for i in itr:
16     print(i)
__iter__,__next__
 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4     def __iter__(self):
 5         return self
 6 
 7     def __next__(self):
 8         if self.x == 0:
 9             raise StopIteration
10         self.x-=1
11         return self.x
12 
13 f1=Foo(5)
14 print(next(f1))
15 print(next(f1))
16 for i in f1:
17     print(i)
__iter__,__next__(2)

 

八、__doc__

返回類的描述信息,這個屬性沒法繼承給子類

 1 class Foo:
 2     """
 3     Hello 我是描述信息
 4     """
 5     def __init__(self):
 6         pass
 7 
 8     def test(self):
 9         pass
10 
11 print(Foo.__doc__)
doc

 

九、__module__和__class__

__module__表示當前操做的對象在哪一個模塊,__class__表示當前操做的對象屬於哪一個類

1 #!/usr/bin/env python
2 
3 class Foo(object):
4 
5     def __init__(self):
6         self.name = "test"
test2
1 from test2 import Foo
2 
3 
4 obj = Foo()
5 
6 print(obj.__module__)
7 print(obj.__class__)
module,class

 

十、__del__

__del__當對象在內存中被釋放時,自動觸發執行,也就是說del 刪除對象和運行結束釋放對象的時候都會執行

 1 class Foo:
 2 
 3     def __del__(self):
 4         print('執行我啦')
 5 
 6 obj = Foo()
 7 obj2 = Foo()
 8 
 9 del obj
10 input(">>>>>>")
del

 

十一、__enter__和__exit__

咱們能夠這樣操做文件 with open(‘a.txt’) as f ,這叫作上下文管理協議,即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__

和__exit__方法。

 1 class Open:
 2     def __init__(self,name):
 3         self.name=name
 4 
 5     def __enter__(self):
 6         print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')
 7         return "test"
 8     def __exit__(self, exc_type, exc_val, exc_tb):
 9         print('with中代碼塊執行完畢時執行我啊')
10 
11 with Open('a.txt') as f:
12     print('f:',f)
enter,exit

 

十二、__call__

__call__表示生成的對象執行對象()就會觸發 call 方法

1 class Foo:
2 
3     def __call__(self, *args, **kwargs):
4         print("__call__")
5 
6 obj = Foo()
7 obj()
call

 

上述方法中其實不少都不經常使用,只須要了解一下便可

 

3、元類(metaclass)

什麼是元類,在Python中一切皆對象,類自己也是對象當使用關鍵字class的時候,python解釋器在加載class的時候就會建立一個對象(這裏的對象指的是類而非類

的實例),咱們可使用type查看元類是誰

1 class Foo:
2     pass
3 print(type(Foo))
查看元類

 

什麼是元類?

元類就是類的祖宗,也就是類的模板

在這個模板中,定義了python是如何建立類的,而且能夠用這個模板建立一個類

1 def func(self,name):
2     print(self)
3     print('from func:',name)
4 
5 Foo=type('Foo',(object,),{'func':func,'x':1})
6 
7 obj = Foo()
8 obj.func("test")
9 print(obj.x)
利用type元類建立類

  可是感受上面好像少點什麼,是否是沒有構造方法

 1 def func(self,):
 2     print(self)
 3     print('from func:',self.name)
 4 
 5 def init(self,name):
 6     self.name = name
 7 Foo=type('Foo',(object,),{'func':func,'x':1,"__init__":init})
 8 
 9 obj = Foo("test")
10 obj.func()
11 print(obj.x)
添加構造方法 

   

  類的建立過程

  好到此爲止已經可使用type元類建立一個類了,可是這個類是若是建立的,type建立類過程是什麼樣子的?

  首先要了解爲何會執行 __init__構造方法,__init__之因此被執行,是由於有__new__來去調用它,__new__裏面幹了就是實例化的過程,當咱們建立一個__new__

  後就直接執行__new__,咱們本身定義的__new__方法中沒有實例化的過程因此沒法實例化,也不能調用 self.name

 1 class Foo(object):
 2     def __init__(self,name):
 3         self.name = name
 4         print("__init__")
 5 
 6     def __new__(cls, *args, **kwargs):
 7         print("__new__")
 8 
 9 f = Foo("test")
10 
11 #報錯
12 #print(f.name) 
__new__

 

那麼若是用自定義的new進行實例化呢?

首先執行 object.__new__(cls)  (cls 就是類的自己),而後觸發 init 以後生成一塊內存地址,而後經過return賦值給實例 f咱們能夠打印一下 object.__new__(cls)

的內存地址,和 f的內存地址,它們的地址是同樣的。

 1 class Foo(object):
 2     def __init__(self,name):
 3         self.name = name
 4         print("__init__")
 5 
 6     def __new__(cls, *args, **kwargs):
 7         print("__new__",*args, **kwargs)
 8         return object.__new__(cls)
 9 
10 f = Foo("test")
11 
12 print(f.name)
自定義new進行實例化

到如今咱們知道了經過 new 調用 init 而後生成內存空間,賦值給實例,那麼這個過程咱們是能夠控制的,看下面的例子

一、首先經過 class Foo (metaclass=MyType)  metaclass指定元類是MyType

二、而後Mytype 創造出Foo這個類對象,創造過程就是先執行 MyType 的 new 而後執行 init

三、而後 Foo(「test」) 就執行了 MyType 的 call方法,在call方法中來定義,Foo實例化的過程,(能夠把註釋的地方解開)

四、這個過程首先經過 Foo的new方法創造實例的內存空間,又執行了obj.age = 22 進行了賦值(這裏爲了體現f.age),而後執行Foo的 init 進行實例化

五、最後經過return 把創造出來的實例的內存空間,返回給 f,這時f就是Foo的實例,(若是不明白能夠加斷點走一變)

 1 class MyType(type):
 2     # def __init__(self,*args,**kwargs):
 3     #     print("MyType __init__",self,*args,**kwargs)
 4 
 5     def __call__(self, *args, **kwargs):
 6         print("MyType __call__")
 7         obj = self.__new__(self)
 8         self.__init__(obj, *args, **kwargs)
 9         obj.age = 25
10         return obj
11 
12     # def __new__(cls, *args, **kwargs):
13     #     print("MyType __new__",)
14     #     return type.__new__(cls,*args, **kwargs)
15 
16 class Foo(metaclass=MyType):
17     def __init__(self,name):
18         self.name = name
19         print("Foo __init__")
20 
21 
22 f = Foo("test")
23 print("查看Foo的類",type(Foo))
24 print(f.name)
25 print(f.age)
建立類的流程

到此咱們演示了 類的建立過程,具體有什麼用,到後期會一一揭開  

 

4、反射

反射的概念是由Smith在1982年首次提出的,主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力(自省)。這一律唸的提出很快引起了計算機科學

領域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和麪向對象方面取得了成績。

反射就是經過類名得到類的實例對象(一切皆對象,因此只要是對象就能用反射),經過方法名獲得方法,實現調用,在python中反射功能是由四個內置函數提供的

  • hasattr 是否含有某成員
  • getattr 獲取成員
  • setattr 設置成員
  • delattr 刪除成員
 1 class Foo(object):
 2 
 3     def __init__(self):
 4         self.name = 'test'
 5 
 6     def func(self):
 7         print("func")
 8         return 'func'
 9 
10 obj = Foo()
11 
12 #### 檢查是否含有成員 ####
13 print(hasattr(obj, 'name'))
14 print(hasattr(obj, 'aaa'))
15 
16 #### 獲取成員 ####
17 print(getattr(obj, 'name'))
18 getattr(obj, 'func')()
19 
20 #### 設置成員 ####
21 setattr(obj, 'age', 18)
22 
23 #### 刪除成員 ####
24 print(obj.__dict__)
25 #注意哦,這裏不能刪除 func 這個方法,由於在 obj中沒有這個方法,這個方法是在 Foo中的
26 delattr(obj, 'name')
27 print(obj.__dict__)
反射的用法

 

   

 

 

做者:北京小遠
出處:http://www.cnblogs.com/bj-xy/ 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索