python 面向對象高級應用(三)

目錄:

  •  isinstance(obj,cls)和issubclass(sub,super)
  • 反射
  • __setattr__,__delattr__,__getattr__
  • 二次加工標準類型(包裝)
  • __getattribute__
  • 描述符(__get__,__set__,__delete__)
  • property
  • __setitem__,__getitem,__delitem__
  • __str__,__repr__,__format__
  • __slots__
  • __next__和__iter__實現迭代器協議
  • __doc__
  • __module__和__class__
  • __del__
  • __enter__和__exit__
  • __call__
  •  metaclass

1、isinstance(obj,cls)和issubclass(sub,super)

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

1 class Foo(object):
2     pass
3  
4 obj = Foo()
5  
6 isinstance(obj, Foo)
View Code

二、issubclass(sub, super)檢查sub類是不是 super 類的派生類程序員

1 class Foo(object):
2     pass
3  
4 class Bar(Foo):
5     pass
6  
7 issubclass(Bar, Foo)
View Code

2、反射

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

二、python面向對象中的反射:經過字符串的形式操做對象相關的屬性。python中的一切事物都是對象(均可以使用反射)數據庫

四個能夠實現自省的函數:編程

hasatter(object, name) : 判斷object中有沒有一個name字符串對應的方法或屬性,檢查成員數組

getattr(object, name, default=None): 獲取成員緩存

setattr(x, y, v):設置成員服務器

delattr(x, y):刪除成員網絡

 1 class Foo(object):
 2  
 3     def __init__(self):
 4         self.name = 'abc'
 5  
 6     def func(self):
 7         return 'ok'
 8  
 9 obj = Foo()
10 #獲取成員
11 ret = getattr(obj, 'func')#獲取的是個對象
12 r = ret()
13 print(r)
14 #檢查成員
15 ret = hasattr(obj,'func')#由於有func方法因此返回True
16 print(ret)
17 #設置成員
18 print(obj.name) #設置以前爲:abc
19 ret = setattr(obj,'name',19)
20 print(obj.name) #設置以後爲:19
21 #刪除成員
22 print(obj.name) #abc
23 delattr(obj,'name')
24 print(obj.name) #報錯
View Code

a.應用實例(一):使用格式數據結構

 1 class BlackMedium:
 2     feature='Ugly'
 3     def __init__(self,name,addr):
 4         self.name=name
 5         self.addr=addr
 6 
 7     def sell_house(self):
 8         print('%s 黑中介賣房子啦,傻逼纔買呢,可是誰能證實本身不傻逼' %self.name)
 9     def rent_house(self):
10         print('%s 黑中介租房子啦,傻逼才租呢' %self.name)
11 
12 b1=BlackMedium('萬成置地','回龍觀天露園')
13 
14 #檢測是否含有某屬性
15 print(hasattr(b1,'name'))
16 print(hasattr(b1,'sell_house'))
17 
18 #獲取屬性
19 n=getattr(b1,'name')
20 print(n)
21 func=getattr(b1,'rent_house')
22 func()
23 
24 # getattr(b1,'aaaaaaaa') #報錯
25 print(getattr(b1,'aaaaaaaa','不存在啊'))
26 
27 #設置屬性
28 setattr(b1,'sb',True)
29 setattr(b1,'show_name',lambda self:self.name+'sb')
30 print(b1.__dict__)
31 print(b1.show_name(b1))
32 
33 #刪除屬性
34 delattr(b1,'addr')
35 delattr(b1,'show_name')
36 delattr(b1,'show_name111')#不存在,則報錯
37 
38 print(b1.__dict__)
View Code

b.應用實例(二):類也是對象,便可以反射

 1 class Foo(object):
 2  
 3     staticField = "old boy"
 4  
 5     def __init__(self):
 6         self.name = 'wupeiqi'
 7  
 8     def func(self):
 9         return 'func'
10  
11     @staticmethod
12     def bar():
13         return 'bar'
14  
15 print getattr(Foo, 'staticField')
16 print getattr(Foo, 'func')
17 print getattr(Foo, 'bar')
View Code

c.應用實例(三):反射當前模塊成員

 1 import sys
 2 
 3 
 4 def s1():
 5     print 's1'
 6 
 7 
 8 def s2():
 9     print 's2'
10 
11 
12 this_module = sys.modules[__name__]
13 
14 hasattr(this_module, 's1')
15 getattr(this_module, 's2')
View Code

d.應用實例(四):導入其餘模塊,利用反射查找該模塊是否存在某個方法

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 def test():
5     print('from the test')
test_module
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3  
 4 """
 5 程序目錄:
 6     module_test.py
 7     index.py
 8  
 9 當前文件:
10     index.py
11 """
12 
13 import module_test as obj
14 
15 #obj.test()
16 
17 print(hasattr(obj,'test'))
18 
19 getattr(obj,'test')()
View Code

三、反射的優勢

a.實現可插拔機制.

有倆程序員,一個lili,一個是egon,lili在寫程序的時候須要用到egon所寫的類,可是egon去跟女友度蜜月去了,尚未完成他寫的類,lili想到了反射,使用了反射機制lili能夠繼續完成本身的代碼,等egon度蜜月回來後再繼續完成類的定義而且去實現lili想要的功能。

總之反射的好處就是,能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能

1 class FtpClient:
2     'ftp客戶端,可是還麼有實現具體的功能'
3     def __init__(self,addr):
4         print('正在鏈接服務器[%s]' %addr)
5         self.addr=addr
View Code
1 #from module import FtpClient
2 f1=FtpClient('192.168.1.1')
3 if hasattr(f1,'get'):
4     func_get=getattr(f1,'get')
5     func_get()
6 else:
7     print('---->不存在此方法')
8     print('處理其餘的邏輯')
View Code 

b.動態導入模塊(基於反射當前模塊成員).

1 '''
2 動態導入模塊
3 路徑
4 |-import_lib
5 --|-metaclass.py
6 '''
7 import importlib  # 導入importlib內置庫
8 
9 importlib.import_module("import_lib.metaclass") #獲得上述路徑文件
View Code

3、__setattr__,__delattr__,__getattr__

  類的屬性操做,__dict__的重寫方法。可修改

一、__setattr__,__delattr__,__getattr__(等同於setattr,delattr,getattr),它的用法以下:

__setattr__ 添加/修改屬性會觸發執行

__delattr__ 刪除屬性的時候會觸發

__getlattr__ 只有在使用點調用屬性且屬性不存在的時候纔會觸發。(新式類獨有)

 1 class Foo:
 2     x=1
 3     def __init__(self,y):
 4         self.y=y
 5 
 6     def __getattr__(self, item):
 7         print('----> from getattr:你找的屬性不存在')
 8 
 9 
10     def __setattr__(self, key, value):
11         print('----> from setattr')
12         # self.key=value #這就無限遞歸了,你好好想一想
13         # self.__dict__[key]=value #應該使用它
14 
15     def __delattr__(self, item):
16         print('----> from delattr')
17         # del self.item #無限遞歸了
18         self.__dict__.pop(item)
19 
20 #__setattr__添加/修改屬性會觸發它的執行
21 f1=Foo(10)
22 print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值
23 f1.z=3
24 print(f1.__dict__)
25 
26 #__delattr__刪除屬性的時候會觸發
27 f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做
28 del f1.a
29 print(f1.__dict__)
30 
31 #__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發
32 f1.xxxxxx
View Code

4、二次加工標準類型(包裝)

一、包裝:

python爲你們提供了標準數據類型,以及豐富的內置方法,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增/改寫方法,這就用到了咱們剛學的繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)如:

1 # List繼承列表類,他繼承了python解釋器list列表的全部功能
2 # class List(list):
3 #     pass
4 #
5 # l=List([1,2,3])
6 # print(l)
7 # l.append(4)
8 # print(l)
View Code

經過繼承,可修改添加爲:

 1 class List(list):
 2     def __init__(self,item,tag=False):
 3         super().__init__(item)
 4         self.tag=tag
 5     # 重寫列表類的append方法,只容許添加str類型,否則報錯
 6     def append(self, p_object):
 7         # print(p_object)
 8         if not isinstance(p_object,str):
 9             raise TypeError('%s must be str' %p_object)
10         super(List,self).append(p_object)
11 
12     # 添加獲取中間值方法,@property可當聲明靜態變量調用
13     @property
14     def mid(self):
15         mid_index=len(self)//2
16         return self[mid_index]
17 
18     # 必須修改tag,才能清空列表
19     def clear(self):
20         if not self.tag:
21             raise PermissionError('not permissive')
22         super().clear()
23         self.tag=False
24 
25 # l=List([1,2,3])
26 # l.append(4)
27 # l.append('aaaaa')
28 # l.append('aaaaa')
29 # print(l)
30         
31 # print(l.mid)
32 # l=[1,2,3,4,5,56,6,7,7]
33 #
34 # mid_index=len(l)//2
35 # print(l[mid_index])
36 
37 # l.insert(0,123123123123123)
38 # print(l)
39 
40 # l.tag=True
41 # l.clear()
42 # print(l)
View Code

二、受權:

受權是包裝的一個特性, 包裝一個類型一般是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能。其它的則保持原樣。受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性。

實現受權的關鍵點就是覆蓋__getattr__方法。主要針對,python標準庫中,不是類屬性的工具模塊如:函數屬性,修改。

範例爲:open方法

 1 import time
 2 class Open:
 3     def __init__(self,filepath,mode='r',encoding='utf-8'):
 4         self.filepath=filepath
 5         self.mode=mode
 6         self.encoding=encoding
 7         self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
 8         
 9     #寫入每行增長日期
10     def write(self,msg):
11         t=time.strftime('%Y-%m-%d %X')
12         self.f.write('%s %s\n' %(t,msg))
13         
14     #其餘除了write的方法,都反射會原來工具處理
15     def __getattr__(self, item):
16         # print(item,type(item))
17         return getattr(self.f,item)
18 obj=Open('a.txt','w+',encoding='utf-8')
View Code

範例二:open方法

 1 # 咱們來加上b模式支持
 2 class FileHandle:
 3     def __init__(self,filename,mode='r',encoding='utf-8'):
 4         if 'b' in mode:
 5             self.file=open(filename,mode)
 6         else:
 7             self.file=open(filename,mode,encoding=encoding)
 8         self.filename=filename
 9         self.mode=mode
10         self.encoding=encoding
11 
12     # 咱們來加上b(二進制)模式支持
13     def write(self,line):
14         if 'b' in self.mode:
15             if not isinstance(line,bytes):
16                 raise TypeError('must be bytes')
17         self.file.write(line)
18 
19     def __getattr__(self, item):
20         return getattr(self.file,item)
21 
22     def __str__(self):
23         if 'b' in self.mode:
24             res="<_io.BufferedReader name='%s'>" %self.filename
25         else:
26             res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
27         return res
28 f1=FileHandle('b.txt','wb')
29 # f1.write('你好啊啊啊啊啊') #自定製的write,不用在進行encode轉成二進制去寫了,簡單,大氣
30 f1.write('你好啊'.encode('utf-8'))
31 print(f1)
32 f1.close()
View Code

練習:使用受權重寫list的方法

 1 #練習一
 2 class List:
 3     def __init__(self,seq):
 4         self.seq=seq
 5 
 6     def append(self, p_object):
 7         ' 派生本身的append加上類型檢查,覆蓋原有的append'
 8         if not isinstance(p_object,int):
 9             raise TypeError('must be int')
10         self.seq.append(p_object)
11 
12     @property
13     def mid(self):
14         '新增本身的方法'
15         index=len(self.seq)//2
16         return self.seq[index]
17 
18     def __getattr__(self, item):
19         return getattr(self.seq,item)
20 
21     def __str__(self):
22         return str(self.seq)
23 
24 l=List([1,2,3])
25 print(l)
26 l.append(4)
27 print(l)
28 # l.append('3333333') #報錯,必須爲int類型
29 
30 print(l.mid)
31 
32 #基於受權,得到insert方法
33 l.insert(0,-123)
34 print(l)
35 
36 
37 
38 
39 
40 #練習二
41 class List:
42     def __init__(self,seq,permission=False):
43         self.seq=seq
44         self.permission=permission
45     def clear(self):
46         if not self.permission:
47             raise PermissionError('not allow the operation')
48         self.seq.clear()
49 
50     def __getattr__(self, item):
51         return getattr(self.seq,item)
52 
53     def __str__(self):
54         return str(self.seq)
55 l=List([1,2,3])
56 # l.clear() #此時沒有權限,拋出異常
57 
58 
59 l.permission=True
60 print(l)
61 l.clear()
62 print(l)
63 
64 #基於受權,得到insert方法
65 l.insert(0,-123)
66 print(l)
View Code

5、__getattribute__

__getattriute__與__getattr_意思相反,無論只要使用點調用屬性無論存不存在,都會執行。且當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError。

 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 
 4 class Foo:
 5     def __init__(self,x):
 6         self.x=x
 7 
 8     def __getattr__(self, item):
 9         print('執行的是我')
10         # return self.__dict__[item]
11     def __getattribute__(self, item):
12         print('無論是否存在,我都會執行')
13         raise AttributeError('哈哈')
14 
15 f1=Foo(10)
16 f1.x
17 f1.xxxxxx
View Code

6、描述符(__get__,__set__,__delete__)

一、描述符

描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議

__get__():調用一個屬性時,觸發
__set__():爲一個屬性賦值時,觸發
__delete__():採用del刪除屬性時,觸發

定義一個描述符

1 class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱做一個描述符
2     def __get__(self, instance, owner):
3         pass
4     def __set__(self, instance, value):
5         pass
6     def __delete__(self, instance):
7         pass
View Code

二、描述符的做用

描述符的做用是用來代理另一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Shu Yang Wang
 4 # Date: 2017/6/19
 5 
 6 
 7 #描述符Str
 8 class Str:
 9     def __get__(self, instance, owner):
10         print('Str調用')
11     def __set__(self, instance, value):
12         print('Str設置...')
13     def __delete__(self, instance):
14         print('Str刪除...')
15 
16 #描述符Int
17 class Int:
18     def __get__(self, instance, owner):
19         print('Int調用')
20     def __set__(self, instance, value):
21         print('Int設置...')
22     def __delete__(self, instance):
23         print('Int刪除...')
24 
25 class People:
26     name=Str()
27     age=Int()
28     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
29         self.name=name
30         self.age=age
31 
32 #何地?:定義成另一個類的類屬性
33 
34 #什麼時候?:且看下列演示
35 
36 p1=People('alex',18)
37 '''
38 Str設置...
39 Int設置...
40 '''
41 
42 #描述符Str的使用
43 p1.name
44 p1.name='egon'
45 del p1.name
46 '''
47 Str調用
48 Str設置...
49 Str刪除...
50 '''
51 
52 #描述符Int的使用
53 p1.age
54 p1.age=18
55 del p1.age
56 '''
57 Int調用
58 Int設置...
59 Int刪除...
60 '''
61 
62 #咱們來瞅瞅到底發生了什麼
63 print(p1.__dict__)
64 print(People.__dict__)
65 '''
66 {}
67 {'__module__': '__main__', 'name': <__main__.Str object at 0x0000009B9AB899E8>, 'age': <__main__.Int object at 0x0000009B9AB89A20>, '__init__': <function People.__init__ at 0x0000009B9AB8BBF8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
68 '''
69 
70 #補充
71 print(type(p1) == People) #type(obj)實際上是查看obj是由哪一個類實例化來的
72 print(type(p1).__dict__ == People.__dict__)
73 '''
74 True
75 True
76 '''
View Code

三、描述符的分類:

a.數據描述符: 至少實現一個__get__() 、__set__()

1 class Foo:
2     def __set__(self, instance, value):
3         print('set')
4     def __get__(self, instance, owner):
5         print('get')
View Code

b.非數據描述符

1 class Foo:
2     def __get__(self, instance, owner):
3         print('get')
View Code

四、使用注意

a.描述符自己應該定義成新式類,被代理的類也應該是新式類

b.必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中

c.要嚴格遵循該優先級,優先級由高到底分別:類屬性--〉數據描述符--〉實例屬性--〉非數據描述符--〉找不到的屬性觸發__getattr__()

1)類屬性--〉數據描述符

 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str調用')
 5     def __set__(self, instance, value):
 6         print('Str設置...')
 7     def __delete__(self, instance):
 8         print('Str刪除...')
 9 
10 class People:
11     name=Str()
12     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
13         self.name=name
14         self.age=age
15 
16 
17 #基於上面的演示,咱們已經知道,在一個類中定義描述符它就是一個類屬性,存在於類的屬性字典中,而不是實例的屬性字典
18 
19 #那既然描述符被定義成了一個類屬性,直接經過類名也必定能夠調用吧,沒錯
20 People.name #恩,調用類屬性name,本質就是在調用描述符Str,觸發了__get__()
21 
22 People.name='egon' #那賦值呢,我去,並無觸發__set__()
23 del People.name #趕忙試試del,我去,也沒有觸發__delete__()
24 #結論:描述符對類沒有做用-------->傻逼到家的結論
25 
26 '''
27 結果:
28 Str調用
29 緣由:描述符在使用時被定義成另一個類的類屬性,於是類屬性比二次加工的描述符假裝而來的類屬性有更高的優先級
30 People.name #恩,調用類屬性name,找不到就去找描述符假裝的類屬性name,觸發了__get__()
31 
32 People.name='egon' #那賦值呢,直接賦值了一個類屬性,它擁有更高的優先級,至關於覆蓋了描述符,確定不會觸發描述符的__set__()
33 del People.name #同上
34 '''
View Code

2)數據描述符--〉實例屬性

 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str調用')
 5     def __set__(self, instance, value):
 6         print('Str設置...')
 7     def __delete__(self, instance):
 8         print('Str刪除...')
 9 
10 class People:
11     name=Str()
12     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
13         self.name=name
14         self.age=age
15 
16 
17 p1=People('egon',18)
18 
19 #若是描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操做,於p1自己無關了,至關於覆蓋了實例的屬性
20 p1.name='egonnnnnn'
21 p1.name
22 print(p1.__dict__)#實例的屬性字典中沒有name,由於name是一個數據描述符,優先級高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了
23 del p1.name
24 '''
25 結果:
26 Str設置...
27 Str設置...
28 Str調用
29 {'age': 18}
30 Str刪除...
31 '''
View Code

3)實例屬性--〉非數據描述符

 1 # class Foo:
 2 #     def __set__(self, instance, value):
 3 #         print('set')
 4 #     def __get__(self, instance, owner):
 5 #         print('get')
 6 # class Room:
 7 #     name=Foo()
 8 #     def __init__(self,name,width,length):
 9 #         self.name=name
10 #         self.width=width
11 #         self.length=length
12 #
13 #
14 # #name是一個數據描述符,由於name=Foo()而Foo實現了get和set方法,於是比實例屬性有更高的優先級
15 # #對實例的屬性操做,觸發的都是描述符的
16 # r1=Room('廁所',1,1)
17 # r1.name
18 # r1.name='廚房'
19 '''
20 set
21 get
22 set
23 '''
24 
25 
26 # class Foo:
27 #     def __get__(self, instance, owner):
28 #         print('get')
29 # class Room:
30 #     name=Foo()
31 #     def __init__(self,name,width,length):
32 #         self.name=name
33 #         self.width=width
34 #         self.length=length
35 
36 
37 # #name是一個非數據描述符,由於name=Foo()而Foo沒有實現set方法,於是比實例屬性有更低的優先級
38 # #對實例的屬性操做,觸發的都是實例本身的
39 # r1=Room('廁所',1,1)
40 # r1.name
41 # r1.name='廚房'
42 '''
43 沒有任何觸發
44 '''
View Code

4)非數據描述符--〉找不到的屬性觸發__getattr__()

1 class Foo:
2     def func(self):
3         print('我胡漢三又回來了')
4 
5     def __getattr__(self, item):
6         print('找不到了固然是來找我啦',item)
7 f1=Foo()
8 
9 f1.xxxxxxxxxxx
View Code

五、描述符使用實例

衆所周知,python是弱類型語言,即參數的賦值沒有類型限制,下面咱們經過描述符機制來實現類型限制功能

 1 # class Str:
 2 #     def __init__(self,name):
 3 #         self.name=name
 4 #     def __get__(self, instance, owner):
 5 #         print('get--->',instance,owner)
 6 #         return instance.__dict__[self.name]
 7 #
 8 #     def __set__(self, instance, value):
 9 #         print('set--->',instance,value)
10 #         instance.__dict__[self.name]=value
11 #
12 #     def __delete__(self, instance):
13 #         print('delete--->',instance)
14 #         instance.__dict__.pop(self.name)
15 #
16 #
17 # class People:
18 #     name=Str('name')
19 #     def __init__(self,name,age,salary):
20 #         self.name=name
21 #         self.age=age
22 #         self.salary=salary
23 #
24 # p1=People('egon',18,3231.3)
25 '''
26 set---> <__main__.People object at 0x0000009A73999A20> egon
27 
28 '''
29 
30 #調用
31 # print(p1.__dict__)
32 # p1.name
33 '''
34 {'name': 'egon', 'age': 18, 'salary': 3231.3}
35 get---> <__main__.People object at 0x0000009A73999A20> <class '__main__.People'>
36 '''
37 
38 #賦值
39 # print(p1.__dict__)
40 # p1.name='egonlin'
41 # print(p1.__dict__)
42 '''
43 {'name': 'egon', 'age': 18, 'salary': 3231.3}
44 set---> <__main__.People object at 0x0000009A73999A20> egonlin
45 {'name': 'egonlin', 'age': 18, 'salary': 3231.3}
46 '''
47 
48 
49 #刪除
50 # print(p1.__dict__)
51 # del p1.name
52 # print(p1.__dict__)
53 
54 '''
55 {'name': 'egonlin', 'age': 18, 'salary': 3231.3}
56 delete---> <__main__.People object at 0x0000009A73999A20>
57 {'age': 18, 'salary': 3231.3}
58 '''
View Code

添加限制功能:

 1 class Typed:
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     def __get__(self, instance, owner):
 6         print('get--->',instance,owner)
 7         if instance is None:
 8             return self
 9         return instance.__dict__[self.name]
10 
11     def __set__(self, instance, value):
12         print('set--->',instance,value)
13         if not isinstance(value,self.expected_type):
14             raise TypeError('Expected %s' %str(self.expected_type))
15         instance.__dict__[self.name]=value
16     def __delete__(self, instance):
17         print('delete--->',instance)
18         instance.__dict__.pop(self.name)
19 
20 
21 class People:
22     name=Typed('name',str)
23     age=Typed('name',int)
24     salary=Typed('name',float)
25     def __init__(self,name,age,salary):
26         self.name=name
27         self.age=age
28         self.salary=salary
29 
30 #p1=People(123,18,3333.3)
31 #p1=People('egon','18',3333.3)
32 p1=People('egon',18,3333.3)
View Code

經過類裝飾器來實現,中級版:

 1 class Typed:
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     def __get__(self, instance, owner):
 6         print('get--->',instance,owner)
 7         if instance is None:
 8             return self
 9         return instance.__dict__[self.name]
10 
11     def __set__(self, instance, value):
12         print('set--->',instance,value)
13         if not isinstance(value,self.expected_type):
14             raise TypeError('Expected %s' %str(self.expected_type))
15         instance.__dict__[self.name]=value
16     def __delete__(self, instance):
17         print('delete--->',instance)
18         instance.__dict__.pop(self.name)
19 
20 def typeassert(**kwargs):
21     def decorate(cls):
22         print('類的裝飾器開始運行啦------>',kwargs)
23         for name,expected_type in kwargs.items():
24             setattr(cls,name,Typed(name,expected_type))
25         return cls
26     return decorate
27 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People)
28 class People:
29     def __init__(self,name,age,salary):
30         self.name=name
31         self.age=age
32         self.salary=salary
33 
34 print(People.__dict__)
35 p1=People('egon',18,3333.3)
View Code

ps.類裝飾器

  a.無參

 1 def decorate(cls):
 2     print('類的裝飾器開始運行啦------>')
 3     return cls
 4 
 5 @decorate #無參:People=decorate(People)
 6 class People:
 7     def __init__(self,name,age,salary):
 8         self.name=name
 9         self.age=age
10         self.salary=salary
11 
12 p1=People('egon',18,3333.3)
View Code

  b.有參

 1 def typeassert(**kwargs):
 2     def decorate(cls):
 3         print('類的裝飾器開始運行啦------>',kwargs)
 4         return cls
 5     return decorate
 6 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People)
 7 class People:
 8     def __init__(self,name,age,salary):
 9         self.name=name
10         self.age=age
11         self.salary=salary
12 
13 p1=People('egon',18,3333.3)
View Code

六、總結

描述符是能夠實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性描述父是不少高級庫和框架的重要工具之一,描述符一般是使用到裝飾器或者元類的大型框架中的一個組件.

七、利用描述符原理完成一個自定製@property,實現延遲計算(本質就是把一個函數屬性利用裝飾器原理作成一個描述符:類的屬性字典中函數名爲key,value爲描述符類產生的對象)

a.@property用法回顧

 1 class Room:
 2     def __init__(self,name,width,length):
 3         self.name=name
 4         self.width=width
 5         self.length=length
 6 
 7     @property
 8     def area(self):
 9         return self.width * self.length
10 
11 r1=Room('alex',1,1)
12 print(r1.area)
View Code

b.本身實現@property功能

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         self.func=func
 4     def __get__(self, instance, owner):
 5         print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()')
 6         if instance is None:
 7             return self
 8         return self.func(instance) #此時你應該明白,究竟是誰在爲你作自動傳遞self的事情
 9 
10 class Room:
11     def __init__(self,name,width,length):
12         self.name=name
13         self.width=width
14         self.length=length
15 
16     @Lazyproperty #area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符
17     def area(self):
18         return self.width * self.length
19 
20 r1=Room('alex',1,1)
21 print(r1.area)
View Code

c.實現延遲計算

 1 class Lazyproperty:
 2     def __init__(self,func):
 3         self.func=func
 4     def __get__(self, instance, owner):
 5         print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()')
 6         if instance is None:
 7             return self
 8         else:
 9             print('--->')
10             value=self.func(instance)
11             setattr(instance,self.func.__name__,value) #計算一次就緩存到實例的屬性字典中
12             return value
13 
14 class Room:
15     def __init__(self,name,width,length):
16         self.name=name
17         self.width=width
18         self.length=length
19 
20     @Lazyproperty #area=Lazyproperty(area) 至關於'定義了一個類屬性,即描述符'
21     def area(self):
22         return self.width * self.length
23 
24 r1=Room('alex',1,1)
25 print(r1.area) #先從本身的屬性字典找,沒有再去類的中找,而後出發了area的__get__方法
26 print(r1.area) #先從本身的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算
View Code

d.去掉裝飾類的返回值,緩存失敗

 1 #緩存不起來了
 2 
 3 class Lazyproperty:
 4     def __init__(self,func):
 5         self.func=func
 6     def __get__(self, instance, owner):
 7         print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()')
 8         if instance is None:
 9             return self
10         else:
11             value=self.func(instance)
12             instance.__dict__[self.func.__name__]=value
13             return value
14         # return self.func(instance) #此時你應該明白,究竟是誰在爲你作自動傳遞self的事情
15     def __set__(self, instance, value):
16         print('hahahahahah')
17 
18 class Room:
19     def __init__(self,name,width,length):
20         self.name=name
21         self.width=width
22         self.length=length
23 
24     @Lazyproperty #area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符
25     def area(self):
26         return self.width * self.length
27 
28 print(Room.__dict__)
29 r1=Room('alex',1,1)
30 print(r1.area)
31 print(r1.area) 
32 print(r1.area) 
33 print(r1.area) #緩存功能失效,每次都去找描述符了,爲什麼,由於描述符實現了set方法,它由非數據描述符變成了數據描述符,數據描述符比實例屬性有更高的優先級,於是全部的屬性操做都去找描述符了
View Code

八、利用描述符原理完成一個自定製@classmethod

 1 class ClassMethod:
 2     def __init__(self,func):
 3         self.func=func
 4 
 5     def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
 6         def feedback():
 7             print('在這裏能夠加功能啊...')
 8             return self.func(owner)
 9         return feedback
10 
11 class People:
12     name='linhaifeng'
13     @ClassMethod # say_hi=ClassMethod(say_hi)
14     def say_hi(cls):
15         print('你好啊,帥哥 %s' %cls.name)
16 
17 People.say_hi()
18 
19 p1=People()
20 p1.say_hi()
21 #疑問,類方法若是有參數呢,好說,好說
22 
23 class ClassMethod:
24     def __init__(self,func):
25         self.func=func
26 
27     def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
28         def feedback(*args,**kwargs):
29             print('在這裏能夠加功能啊...')
30             return self.func(owner,*args,**kwargs)
31         return feedback
32 
33 class People:
34     name='linhaifeng'
35     @ClassMethod # say_hi=ClassMethod(say_hi)
36     def say_hi(cls,msg):
37         print('你好啊,帥哥 %s %s' %(cls.name,msg))
38 
39 People.say_hi('你是那偷心的賊')
40 
41 p1=People()
42 p1.say_hi('你是那偷心的賊')
View Code

九、利用描述符原理完成一個自定製的@staticmethod

 1 class StaticMethod:
 2     def __init__(self,func):
 3         self.func=func
 4 
 5     def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
 6         def feedback(*args,**kwargs):
 7             print('在這裏能夠加功能啊...')
 8             return self.func(*args,**kwargs)
 9         return feedback
10 
11 class People:
12     @StaticMethod# say_hi=StaticMethod(say_hi)
13     def say_hi(x,y,z):
14         print('------>',x,y,z)
15 
16 People.say_hi(1,2,3)
17 
18 p1=People()
19 p1.say_hi(4,5,6)
View Code

7、property:

一個靜態屬性property本質就是實現了get,set,delete三種方法。

一、用法:

 1 class Foo:
 2     @property
 3     def AAA(self):
 4         print('get的時候運行我啊')
 5 
 6     @AAA.setter
 7     def AAA(self,value):
 8         print('set的時候運行我啊')
 9 
10     @AAA.deleter
11     def AAA(self):
12         print('delete的時候運行我啊')
13 
14 #只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
15 f1=Foo()
16 f1.AAA
17 f1.AAA='aaa'
18 del f1.AAA
View Code

二、老式用法:

 1 class Foo:
 2     def get_AAA(self):
 3         print('get的時候運行我啊')
 4 
 5     def set_AAA(self,value):
 6         print('set的時候運行我啊')
 7 
 8     def delete_AAA(self):
 9         print('delete的時候運行我啊')
10     AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應
11 
12 f1=Foo()
13 f1.AAA
14 f1.AAA='aaa'
15 del f1.AAA
View Code

  三、實例:

 1 class Goods:
 2 
 3     def __init__(self):
 4         # 原價
 5         self.original_price = 100
 6         # 折扣
 7         self.discount = 0.8
 8 
 9     @property
10     def price(self):
11         # 實際價格 = 原價 * 折扣
12         new_price = self.original_price * self.discount
13         return new_price
14 
15     @price.setter
16     def price(self, value):
17         self.original_price = value
18 
19     @price.deleter
20     def price(self):
21         del self.original_price
22 
23 
24 obj = Goods()
25 obj.price         # 獲取商品價格
26 obj.price = 200   # 修改商品原價
27 print(obj.price)
28 del obj.price     # 刪除商品原價
View Code
 1 #實現類型檢測功能
 2 
 3 #第一關:
 4 class People:
 5     def __init__(self,name):
 6         self.name=name
 7 
 8     @property
 9     def name(self):
10         return self.name
11 
12 # p1=People('alex') #property自動實現了set和get方法屬於數據描述符,比實例屬性優先級高,因此你這面寫會觸發property內置的set,拋出異常
13 
14 
15 #第二關:修訂版
16 
17 class People:
18     def __init__(self,name):
19         self.name=name #實例化就觸發property
20 
21     @property
22     def name(self):
23         # return self.name #無限遞歸
24         print('get------>')
25         return self.DouNiWan
26 
27     @name.setter
28     def name(self,value):
29         print('set------>')
30         self.DouNiWan=value
31 
32     @name.deleter
33     def name(self):
34         print('delete------>')
35         del self.DouNiWan
36 
37 p1=People('alex') #self.name實際是存放到self.DouNiWan裏
38 print(p1.name)
39 print(p1.name)
40 print(p1.name)
41 print(p1.__dict__)
42 
43 p1.name='egon'
44 print(p1.__dict__)
45 
46 del p1.name
47 print(p1.__dict__)
48 
49 
50 #第三關:加上類型檢查
51 class People:
52     def __init__(self,name):
53         self.name=name #實例化就觸發property
54 
55     @property
56     def name(self):
57         # return self.name #無限遞歸
58         print('get------>')
59         return self.DouNiWan
60 
61     @name.setter
62     def name(self,value):
63         print('set------>')
64         if not isinstance(value,str):
65             raise TypeError('必須是字符串類型')
66         self.DouNiWan=value
67 
68     @name.deleter
69     def name(self):
70         print('delete------>')
71         del self.DouNiWan
72 
73 p1=People('alex') #self.name實際是存放到self.DouNiWan裏
74 p1.name=1
View Code

8、__setitem__,__getitem,__delitem__

在Python中,若是咱們想實現建立相似於序列和映射的類,能夠經過重寫魔法方法__getitem__、__setitem__、__delitem__、__len__方法去模擬。

__getitem__(self,key):返回鍵對應的值。

__setitem__(self,key,value):設置給定鍵的值

__delitem__(self,key):刪除給定鍵對應的元素。

__len__():返回元素的數量

 1 class Foo:
 2     def __init__(self,name):
 3         self.name=name
 4     def __getitem__(self, item):
 5         print("getitem")
 6         return self.__dict__[item]
 7     def __setitem__(self, key, value):
 8         print("setitem",key,value)
 9         self.__dict__[key]=value
10     def __delitem__(self, key):
11         print('del obj[key]時,我執行')
12         self.__dict__.pop(key)
13 obj=Foo('shuyang')
14 obj.name='shuyang666'
15 print(obj.name)
16 '''
17 shuyang666
18 '''
19 print('-----------')
20 obj['name']='shuyang666'
21 '''
22 setitem name shuyang666
23 '''
24 print(obj['name'])
25 '''
26 getitem
27 shuyang666
28 '''
29 # print(obj.name)
30 del obj['name']
31 print(obj.__dict__)
32 '''
33 del obj[key]時,我執行
34 {}
35 '''
View Code

9、__str__,__repr__,__format__

  一、__str__, __repr__, __format__ 方法

改變對象的字符串顯示__str__,__repr__

自定製格式化字符串__format__

 1 ormat_dict={
 2     'nat':'{obj.name}-{obj.addr}-{obj.type}',#學校名-學校地址-學校類型
 3     'tna':'{obj.type}:{obj.name}:{obj.addr}',#學校類型:學校名:學校地址
 4     'tan':'{obj.type}/{obj.addr}/{obj.name}',#學校類型/學校地址/學校名
 5 }
 6 class School:
 7     def __init__(self,name,addr,type):
 8         self.name=name
 9         self.addr=addr
10         self.type=type
11 
12     def __repr__(self):
13         return 'School(%s,%s)' %(self.name,self.addr)
14     def __str__(self):
15         return '(%s,%s)' %(self.name,self.addr)
16 
17     def __format__(self, format_spec):
18         # if format_spec
19         if not format_spec or format_spec not in format_dict:
20             format_spec='nat'
21         fmt=format_dict[format_spec]
22         return fmt.format(obj=self)
23 
24 s1=School('oldboy1','北京','私立')
25 print('from repr: ',repr(s1))
26 print('from str: ',str(s1))
27 print(s1)
28 
29 '''
30 str函數或者print函數--->obj.__str__()
31 repr或者交互式解釋器--->obj.__repr__()
32 若是__str__沒有被定義,那麼就會使用__repr__來代替輸出
33 注意:這倆方法的返回值必須是字符串,不然拋出異常
34 '''
35 print(format(s1,'nat'))
36 print(format(s1,'tna'))
37 print(format(s1,'tan'))
38 print(format(s1,'asfdasdffd'))
View Code

二、issubclass和isinstance

 1 class A:
 2     pass
 3 
 4 class B(A):
 5     pass
 6 
 7 print(issubclass(B,A)) #B是A的子類,返回True
 8 
 9 a1=A()
10 print(isinstance(a1,A)) #a1是A的實例
View Code

10、__slots__

一、__slots__:

是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性)。使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的)

二、__slots__的應用場景:

字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__。當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個。字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。

三、注意事項:

__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。更多的是用來做爲一個內存優化工具。

四、寫法示例:

 1 class Foo:
 2     __slots__='x'
 3 
 4 
 5 f1=Foo()
 6 f1.x=1
 7 f1.y=2#報錯
 8 print(f1.__slots__) #f1再也不有__dict__
 9 
10 class Bar:
11     __slots__=['x','y']
12     
13 n=Bar()
14 n.x,n.y=1,2
15 n.z=3#報錯
View Code

五、具體詳解:

http://blog.csdn.net/sxingming/article/details/52892640

11、__next__和__iter__實現迭代器協議

在__iter__函數中將使__next__中的StopIteration raise的條件歸零,則能夠循環迭代實例。

一、自增:

 1 class Foo:
 2     def __init__(self,start,stop):
 3         self.num=start
 4         self.stop=stop
 5     def __iter__(self):
 6         return self
 7     def __next__(self):
 8         if self.num >= self.stop:
 9             raise StopIteration   # for 不報錯
10         n=self.num
11         self.num+=1
12         return n
13 
14 f=Foo(1,5)
15 from collections import Iterable,Iterator 
16 print(isinstance(f,Iterator)) #是不是一個迭代器
17 
18 for i in Foo(1,5):
19     print(i) 
View Code

二、range步長:

 1 class Range:
 2     def __init__(self,n,stop,step):
 3         self.n=n
 4         self.stop=stop
 5         self.step=step
 6 
 7     def __next__(self):
 8         if self.n >= self.stop:
 9             raise StopIteration
10         x=self.n
11         self.n+=self.step
12         return x
13 
14     def __iter__(self):
15         return self
16 
17 for i in Range(1,7,3): #
18     print(i)
View Code

三、斐波那契數列:

 1 class Fib:
 2     def __init__(self):
 3         self._a=0
 4         self._b=1
 5 
 6     def __iter__(self):
 7         return self
 8 
 9     def __next__(self):
10         self._a,self._b=self._b,self._a + self._b
11         return self._a
12 
13 f1=Fib()
14 
15 print(f1.__next__())
16 print(next(f1))
17 print(next(f1))
18 
19 for i in f1:
20     if i > 100:
21         break
22     print('%s ' %i,end='')
23 
24 斐波那契數列
View Code

12、__doc__

一、顯示‘ ’ ‘’‘ ’‘’註釋信息

1 class Foo:
2     '我是描述信息'
3     pass
4 
5 print(Foo.__doc__)
View Code

二、該屬性沒法被繼承

1 class Foo:
2     '我是描述信息'
3     pass
4 
5 class Bar(Foo):
6     pass
7 print(Bar.__doc__) #該屬性沒法繼承給子類
View Code

十3、__module__和__class__

__module__ 表示當前操做的對象在那個模塊

__class__     表示當前操做的對象的類是什麼

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 class C:
5 
6     def __init__(self):
7         self.name = ‘SB'
View Code
1 from lib.aa import C
2 
3 obj = C()
4 print obj.__module__  # 輸出 lib.aa,即:輸出模塊
5 print obj.__class__      # 輸出 lib.aa.C,即:輸出類
View Code

 

十4、__del__

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

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

 1 class Foo:
 2 
 3     def __del__(self):
 4         print('執行我啦')
 5 
 6 f1=Foo()
 7 del f1
 8 print('------->')
 9 
10 #輸出結果
11 執行我啦
12 ------->
View Code

十5、__enter__和__exit__

一、上下文管理協議__enter__、 __exit__(with open('a.txt') as f:)

即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法

 1 # class Foo:
 2 #     def __init__(self, name):
 3 #         self.name = name
 4 #     def __enter__(self):
 5 #         print('__enter__')
 6 #         return self
 7 #
 8 #     def __exit__(self, exc_type, exc_val, exc_tb):
 9 #         print('__exit__')
10 #
11 #
12 # with Foo() as x: #x=Foo()
13 #     print(x)
14 #     print('=>')
15 #     print('=>')
16 #     print('=>')
17 #     print('=>')
View Code

二、模擬with...open

 1 class Open:
 2     def __init__(self,filepath,mode='r',encoding='utf-8'):
 3         self.filepath=filepath
 4         self.mode=mode
 5         self.encoding=encoding
 6 
 7     def __enter__(self):
 8         # print('enter')
 9         self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
10         return self.f
11 
12     def __exit__(self, exc_type, exc_val, exc_tb):
13         # print('exit')
14         self.f.close()
15         return True 
16     def __getattr__(self, item):
17         return getattr(self.f,item)
18 
19 with Open('a.txt','w') as f:
20     print(f)
21     f.write('aaaaaa')
22     f.wasdf #拋出異常,交給__exit__處理
View Code

ps.__exit__()中的三個參數分別表明異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都沒法執行

若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行。

三、用途:

a.使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工做,無須手動干預

b.在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在__exit__中定製自動釋放資源的機制,你無須再去關係這個問題,這將大有用處

十6、__call__

一、__call__:

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

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

 1 class Foo:
 2 
 3     def __init__(self):
 4         pass
 5     
 6     def __call__(self, *args, **kwargs):
 7 
 8         print('__call__')
 9 
10 
11 obj = Foo() # 執行 __init__
12 obj()       # 執行 __call__
View Code

十7、metaclass(元類)

一、exec(srt, global,local)的內置方法:

參數一:字符串形式的命令

參數二:全局做用域

參數三:局部做用域

exec會在指定的局部做用域內執行字符串內的代碼,除非明確地使用global關鍵字。

 1 g={
 2     'x':1,
 3     'y':2,
 4     'teachers':{'egon','alex','yuanhao','wupeiqi'}
 5 }
 6 l={'birds':[1,2,3]}
 7 
 8 # exec("""
 9 # def func():
10 #     global x
11 #     x='egon'
12 # func()
13 # """,g,l)
14 # print(g)
15 # print(l)
16 
17 exec("""
18 global x
19 x='egon'
20 """,g,l)
21 print(g)
22 print(l)
View Code

二、類也是對象

python中一切皆是對象,類自己也是一個對象,當使用關鍵字class的時候,python解釋器在加載class的時候就會建立一個對象(這裏的對象指的是類而非類的實例),於是咱們能夠將類看成一個對象去使用,一樣知足第一類對象的概念,能夠:

    • 把類賦值給一個變量
    • 把類做爲函數參數進行傳遞
    • 把類做爲函數的返回值
    • 在運行時動態地建立類
 1 # class Foo:
 2 #     pass
 3 
 4 
 5 # obj=Foo()
 6 # print(type(obj))
 7 #
 8 # print(type(Foo))
 9 # def func():
10 #     class Foo:
11 #         pass
12 #
13 #     print(Foo)
14 #
15 # func()
View Code

三、元類:

元類是類的類,是類的模板。元類是用來控制如何建立類的,正如類是建立對象的模板同樣,而元類的主要目的是爲了控制類的建立行爲。元類的實例化的結果爲咱們用class定義的類,正如類的實例爲對象(f1對象是Foo類的一個實例,Foo類是 type 類的一個實例)type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象

四、建立類的兩種方式

a.使用class關鍵字:

 1 # class Chinese(object):
 2 #     country='China'
 3 #     def __init__(self,name,age):
 4 #         self.name=name
 5 #         self.age=age
 6 #     def talk(self):
 7 #         print('%s is talking' %self.name)
 8 # print(Chinese)
 9 # print(Chinese.__dict__)
10 # print(Chinese.__bases__)
View Code

b.手動使用tpye建立類。

準備工做:

建立類主要分爲三部分
  1 類名
  2 類的父類
  3 類體

 1 #類名
 2 class_name='Chinese'
 3 #類的父類
 4 class_bases=(object,)
 5 #類體
 6 class_body="""
 7 country='China'
 8 def __init__(self,name,age):
 9     self.name=name
10     self.age=age
11 def talk(self):
12     print('%s is talking' %self.name)
13 """
View Code

第一步:生成名稱空間

1 class_dic={}
2 exec(class_body,globals(),class_dic)
3 print(class_dic)
4 #{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
View Code

第二步:調用元類type(也能夠自定義)來產生類Chinense

 1 Foo=type(class_name,class_bases,class_dic) #實例化type獲得對象Foo,即咱們用class定義的類Foo
 2 
 3 print(Foo)
 4 print(type(Foo))
 5 print(isinstance(Foo,type))
 6 '''
 7 <class '__main__.Chinese'>
 8 <class 'type'>
 9 True
10 '''
View Code

ps.這樣咱們就手動生成了一個可調用的類對象。

type 接收三個參數:

第 1 個參數是字符串 ‘Foo’,表示類名
第 2 個參數是元組 (object, ),表示全部的父類
第 3 個參數是字典,這裏是一個空字典,表示沒有定義屬性和方法

補充:若Foo類有繼承,即class Foo(Bar):.... 則等同於type('Foo',(Bar,),{})

五、一個類沒有聲明本身的元類,默認他的元類就是type,除了使用元類type,用戶也能夠經過繼承type來自定義元類(順便咱們也能夠瞅一瞅元類如何控制類的建立,工做流程是什麼)

因此類實例化的流程都同樣,與三個方法有關:(大前提,任何名字後加括號,都是在調用一個功能,觸發一個函數的執行,獲得一個返回值)

a.obj=Foo(),會調用產生Foo的類內的__call__方法,Foo()的結果即__call__的結果
b.調用__call__方法的過程當中,先調用Foo.__new__,獲得obj,即實例化的對象,可是尚未初始化
c.調用__call__方法的過程當中,若是Foo.__new__()返回了obj,再調用Foo.__init__,將obj傳入,進行初始化(不然不調用Foo.__init__)

ps:

__new__更像是其餘語言中的構造函數,必須有返回值,返回值就實例化的對象
__init__只是初始化函數,必須沒有返回值,僅僅只是初始化功能,並不能new建立對象

a.在咱們自定義的元類內,__new__方法在產生obj時用type.__new__(cls,*args,**kwargs),用object.__new__(cls)拋出異常:TypeError: object.__new__(Mymeta) is not safe, use type.__new__()
b.在咱們自定義的類內,__new__方法在產生obj時用object.__new__(self)

六、元類控制建立類:

 1 class Mymeta(type):
 2     def __init__(self):
 3         print('__init__')
 4 
 5     def __new__(cls, *args, **kwargs):
 6         print('__new__')
 7 
 8     def __call__(self, *args, **kwargs):
 9         print('__call__')
10 
11 class Foo(metaclass=Mymeta):
12     pass
13 
14 print(Foo)
15 '''
16 打印結果:
17 __new__
18 None
19 '''
20 
21 '''
22 分析Foo的產生過程,即Foo=Mymeta(),會觸發產生Mymeta的類內的__call__,即元類的__call__:
23     Mymeta加括號,會觸發父類的__call__,即type.__call__
24     在type.__call__裏會調用Foo.__new__
25     而Foo.__new__內只是打印操做,沒有返回值,於是Mymeta的結果爲None,即Foo=None
26 '''
View Code
 1 class Mymeta(type):
 2     def __init__(self,name,bases,dic):
 3         for key,value in dic.items():
 4             if key.startswith('__'):
 5                 continue
 6             if not callable(value):
 7                 continue
 8             if not value.__doc__:
 9                 raise TypeError('%s 必須有文檔註釋' %key)
10         type.__init__(self,name,bases,dic)
11 
12     def __new__(cls, *args, **kwargs):
13         # print('__new__')
14         obj=type.__new__(cls,*args,**kwargs)
15         return obj
16 
17     def __call__(self, *args, **kwargs):
18         # print('__call__')
19         pass
20 
21 # Foo=Mymeta()
22 class Foo(metaclass=Mymeta):
23     def f1(self):
24         'from f1'
25         pass
26 
27     def f2(self):
28         pass
29 
30 '''
31 拋出異常
32 TypeError: f2 必須有文檔註釋
33 '''
View Code

七、元類控制類建立對象

 1 class Mymeta(type):
 2     def __call__(self, *args, **kwargs):
 3         #self=<class '__main__.Foo'>
 4         #args=('egon',)
 5         #kwargs={'age':18}
 6         obj=self.__new__(self) #建立對象:Foo.__new__(Foo)
 7         self.__init__(obj,*args,**kwargs) #初始化對象:Foo.__init__(obj,'egon',age=18)
 8         return obj #必定不要忘記return 
 9 class Foo(metaclass=Mymeta):
10     def __init__(self,name,age):
11         self.name=name
12         self.age=age
13 
14     def __new__(cls, *args, **kwargs):
15         return object.__new__(cls,*args,**kwargs)
16 
17 obj=Foo('egon',age=18) #觸發Mymeta.__call__
18 
19 print(obj.__dict__)
View Code

ps.單例模式:好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存

 1 #單例模式,好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存
 2 class Mysql:
 3     __instance=None
 4     def __init__(self,host='127.0.0.1',port='3306'):
 5         self.host=host
 6         self.port=port
 7 
 8     @classmethod
 9     def singleton(cls,*args,**kwargs):
10         if not cls.__instance:
11             cls.__instance=cls(*args,**kwargs)
12         return cls.__instance
13 
14 
15 obj1=Mysql()
16 obj2=Mysql()
17 print(obj1 is obj2) #False
18 
19 obj3=Mysql.singleton()
20 obj4=Mysql.singleton()
21 print(obj3 is obj4) #True
View Code
 1 #單例模式,好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存
 2 class Mymeta(type):
 3     def __init__(self,name,bases,dic): #定義類Mysql時就觸發
 4         self.__instance=None
 5         super().__init__(name,bases,dic)
 6 
 7     def __call__(self, *args, **kwargs): #Mysql(...)時觸發
 8 
 9         if not self.__instance:
10             self.__instance=object.__new__(self) #產生對象
11             self.__init__(self.__instance,*args,**kwargs) #初始化對象
12             #上述兩步能夠合成下面一步
13             # self.__instance=super().__call__(*args,**kwargs)
14 
15         return self.__instance
16 class Mysql(metaclass=Mymeta):
17     def __init__(self,host='127.0.0.1',port='3306'):
18         self.host=host
19         self.port=port
20 
21 
22 obj1=Mysql()
23 obj2=Mysql()
24 
25 print(obj1 is obj2)
View Code

八、自制元類:

 1 class Mytype(type):
 2     def __init__(self,class_name,bases=None,dict=None):
 3         print("Mytype init--->")
 4         print(class_name,type(class_name))
 5         print(bases)
 6         print(dict)
 7 
 8     def __call__(self, *args, **kwargs):
 9         print('Mytype call---->',self,args,kwargs)
10         obj=self.__new__(self)
11         self.__init__(obj,*args,**kwargs)
12         return obj
13 
14 class Foo(object,metaclass=Mytype):#in python3
15     #__metaclass__ = MyType #in python2
16     x=1111111111
17     def __init__(self,name):
18         self.name=name
19 
20     def __new__(cls, *args, **kwargs):
21         return super().__new__(cls)
22         # return object.__new__(cls) #同上
23 
24 
25 f1=Foo('name')
26 print(f1.__dict__)
View Code
 1 #純淨版
 2 class Mytype(type):
 3     def __init__(self,what,bases=None,dict=None):
 4         print('mytype init')
 5 
 6     def __call__(self, *args, **kwargs):
 7         obj=self.__new__(self)
 8         self.__init__(obj,*args,**kwargs)
 9         return obj
10 
11 class Foo(object,metaclass=Mytype):
12     x=1111111111
13 
14     def __init__(self,name):
15         self.name=name
16 
17     def __new__(cls, *args, **kwargs):
18         return super().__new__(cls)
19 
20 f1=Foo('egon')
21 
22 print(f1.__dict__)
View Code
 1 #精簡版
 2 class Mytype(type):
 3     def __init__(self,what,bases=None,dict=None):
 4         print(what,bases,dict)
 5 
 6     def __call__(self, *args, **kwargs):
 7         print('--->')
 8         obj=object.__new__(self)
 9         self.__init__(obj,*args,**kwargs)
10         return obj
11 class Room(metaclass=Mytype):
12     def __init__(self,name):
13         self.name=name
14 
15 r1=Room('alex')
16 print(r1.__dict__)
View Code

九、元類總結:

 1 #元類總結
 2 class Mymeta(type):
 3     def __init__(self,name,bases,dic):
 4         print('===>Mymeta.__init__')
 5 
 6 
 7     def __new__(cls, *args, **kwargs):
 8         print('===>Mymeta.__new__')
 9         return type.__new__(cls,*args,**kwargs)
10 
11     def __call__(self, *args, **kwargs):
12         print('aaa')
13         obj=self.__new__(self)
14         self.__init__(self,*args,**kwargs)
15         return obj
16 
17 class Foo(object,metaclass=Mymeta):
18     def __init__(self,name):
19         self.name=name
20     def __new__(cls, *args, **kwargs):
21         return object.__new__(cls)
22 
23 '''
24 須要記住一點:名字加括號的本質(即,任何name()的形式),都是先找到name的爹,而後執行:爹.__call__
25 
26 而爹.__call__通常作兩件事:
27 1.調用name.__new__方法並返回一個對象
28 2.進而調用name.__init__方法對兒子name進行初始化
29 '''
30 
31 '''
32 class 定義Foo,並指定元類爲Mymeta,這就至關於要用Mymeta建立一個新的對象Foo,因而至關於執行
33 Foo=Mymeta('foo',(...),{...})
34 所以咱們能夠看到,只定義class就會有以下執行效果
35 ===>Mymeta.__new__
36 ===>Mymeta.__init__
37 實際上class Foo(metaclass=Mymeta)是觸發了Foo=Mymeta('Foo',(...),{...})操做,
38 遇到了名字加括號的形式,即Mymeta(...),因而就去找Mymeta的爹type,而後執行type.__call__(...)方法
39 因而觸發Mymeta.__new__方法獲得一個具體的對象,而後觸發Mymeta.__init__方法對對象進行初始化
40 '''
41 
42 '''
43 obj=Foo('egon')
44 的原理同上
45 '''
46 
47 '''
48 總結:元類的難點在於執行順序很繞,其實咱們只須要記住兩點就能夠了
49 1.誰後面跟括號,就從誰的爹中找__call__方法執行
50 type->Mymeta->Foo->obj
51 Mymeta()觸發type.__call__
52 Foo()觸發Mymeta.__call__
53 obj()觸發Foo.__call__
54 2.__call__內按前後順序依次調用兒子的__new__和__init__方法
55 '''
View Code

十、元類示例:

a.在元類中控制把自定義類的數據屬性都變成大寫

 1 class Mymetaclass(type):
 2     def __new__(cls,name,bases,attrs):
 3         update_attrs={}
 4         for k,v in attrs.items():
 5             if not callable(v) and not k.startswith('__'):
 6                 update_attrs[k.upper()]=v
 7             else:
 8                 update_attrs[k]=v
 9         return type.__new__(cls,name,bases,update_attrs)
10 
11 class Chinese(metaclass=Mymetaclass):
12     country='China'
13     tag='Legend of the Dragon' #龍的傳人
14     def walk(self):
15         print('%s is walking' %self.name)
16 
17 
18 print(Chinese.__dict__)
19 '''
20 {'__module__': '__main__',
21  'COUNTRY': 'China', 
22  'TAG': 'Legend of the Dragon',
23  'walk': <function Chinese.walk at 0x0000000001E7B950>,
24  '__dict__': <attribute '__dict__' of 'Chinese' objects>,                                         
25  '__weakref__': <attribute '__weakref__' of 'Chinese' objects>,
26  '__doc__': None}
27 '''
View Code

b.在元類中控制自定義的類無需__init__方法

1.元類幫其完成建立對象,以及初始化操做;
2.要求實例化時傳參必須爲關鍵字形式,不然拋出異常TypeError: must use keyword argument for key function;
3.key做爲用戶自定義類產生對象的屬性,且全部屬性變成大寫

 1 class Mymetaclass(type):
 2     # def __new__(cls,name,bases,attrs):
 3     #     update_attrs={}
 4     #     for k,v in attrs.items():
 5     #         if not callable(v) and not k.startswith('__'):
 6     #             update_attrs[k.upper()]=v
 7     #         else:
 8     #             update_attrs[k]=v
 9     #     return type.__new__(cls,name,bases,update_attrs)
10 
11     def __call__(self, *args, **kwargs):
12         if args:
13             raise TypeError('must use keyword argument for key function')
14         obj = object.__new__(self) #建立對象,self爲類Foo
15 
16         for k,v in kwargs.items():
17             obj.__dict__[k.upper()]=v
18         return obj
19 
20 class Chinese(metaclass=Mymetaclass):
21     country='China'
22     tag='Legend of the Dragon' #龍的傳人
23     def walk(self):
24         print('%s is walking' %self.name)
25 
26 
27 p=Chinese(name='egon',age=18,sex='male')
28 print(p.__dict__)
View Code
相關文章
相關標籤/搜索