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

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

1、isinstance(obj,cls)检查是否obj是否是类 cls 的对象

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

2、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

二、反射

1、反射定义:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

2、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

3、反射的优点

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

三、__setattr__,__delattr__,__getattr__

  类的属性操作,__dict__的重写方法。可修改

1、__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

四、二次加工标准类型(包装)

1、包装:

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

2、授权:

授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__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

五、__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

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

1、描述符

描述符本质就是一个新式类,在这个新式类中,至少实现了__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

2、描述符的作用

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

 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

3、描述符的分类:

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

4、使用注意

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

5、描述符使用实例

众所周知,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

6、总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

7、利用描述符原理完成一个自定制@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

8、利用描述符原理完成一个自定制@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

9、利用描述符原理完成一个自定制的@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

七、property:

一个静态属性property本质就是实现了get,set,delete三种方法。

1、用法:

 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

2、老式用法:

 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

  3、实例:

 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

八、__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

九、__str__,__repr__,__format__

  1、__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

2、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

十、__slots__

1、__slots__:

是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)。使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

2、__slots__的应用场景:

字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个。字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

3、注意事项:

__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

4、写法示例:

 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

5、具体详解:

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

十一、__next__和__iter__实现迭代器协议

在__iter__函数中将使__next__中的StopIteration raise的条件归零,则可以循环迭代实例。

1、自增:

 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

2、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

3、斐波那契数列:

 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

十二、__doc__

1、显示‘ ’ ‘’‘ ’‘’注释信息

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

2、该属性无法被继承

1 class Foo:
2     '我是描述信息'
3     pass
4 
5 class Bar(Foo):
6     pass
7 print(Bar.__doc__) #该属性无法继承给子类
View Code

十三、__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

 

十四、__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

十五、__enter__和__exit__

1、上下文管理协议__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

2、模拟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后的语句正常执行。

3、用途:

a.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

b.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

十六、__call__

1、__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

十七、metaclass(元类)

1、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

2、类也是对象

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

3、元类:

元类是类的类,是类的模板。元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

4、创建类的两种方式

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,),{})

5、一个类没有声明自己的元类,默认他的元类就是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)

6、元类控制创建类:

 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

7、元类控制类创建对象

 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

8、自制元类:

 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

9、元类总结:

 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

10、元类示例:

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
posted @ 2017-06-15 09:55  shuyang  阅读(341)  评论(0编辑  收藏  举报