zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    在类的外面调用类的private函数
    Django多表操作
    Django聚合与分组查询中value与annotate的顺序问题
    Django路由控制
    cookie与session的区别与关系
    面试题之改变对象的类
    python实现双向链表
    python实现单向循环链表
    python中的顺序表
    顺序表
  • 原文地址:https://www.cnblogs.com/wangshuyang/p/6994722.html
Copyright © 2011-2022 走看看