python学习笔记之魔术方法、类装饰器、单例
魔术方法 
对象的创建与销毁 
__new__创建对象__init__初始化对象__del__当销毁对象时调用 
以一个例子来说明上边各个魔术方法在哪时被调用,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 class  A :    def  __new__ (cls ):         print('call __new__' )         return  object.__new__(cls)          def  __init__ (self ):         print('call __init__' )              def  mothod (self ):         print('call mothod' )              def  __del__ (self ):         print('call __del__' ) 
call __new__
call __init__
从上边输出可知__new__和__init__方法是在类实例化时就被执行了。
call mothod
__del__方法是在对象被销毁时被执行,即是垃圾回收时,这里可以用del来模拟垃圾回收时删除实例a这个对象,如下:
call __del__
在实际编程中,使用最多的是__init__方法
可视化对象 
__repr__   对应repr(object)这个函数,返回一个可以用来表示对象的可打印字符串__str__    对应str(object)这个函数,返回一个字符串对象,适合用于print输出__bytes__   对应bytes(object)这个函数,返回bytes对象 
1 2 3 4 5 6 7 8 9 10 11 12 class  B :    def  __init__ (self,name ):         self.name = name              def  __repr__ (self ):         return  'call __repr__ name is {0}' .format(self.name)          def  __str__ (self ):         return  'call __str__ name is {0}' .format(self.name)          def  __bytes__ (self ):         return  'call __bytes__ name is {0}' .format(self.name).encode('utf-8' ) 
call __repr__ name is zhaochj
call __str__ name is zhaochj
'call __str__ name is zhaochj'
b'call __bytes__ name is zhaochj'
比较运算符重载 
__lt__  小于__le__  小于等于__eq__  等于__ne__  不等于__gt__  大于__ge__  大于等于 
先看下边这个类:
1 2 3 class  Person :    def  __init__ (self,age ):         self.age = age 
True
上边对实例变量可以进行比较,如果想对实例对象进行比较呢?如下:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-42-64d46152e12c> in <module>()
----> 1 p1 > p2
TypeError: unorderable types: Person() > Person()
抛出了TypeError,如果要实例能对实例对象进行比较,那要实现一些方法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class  Person_1 :    def  __init__ (self,age ):         self.age = age              def  __lt__ (self,other ):         print('call __lt__' )         return  self.age < other.age          def  __le__ (self,other ):         print('call __le__' )         return  self.age <= other.age          def  __eq__ (self,other ):         print('call __eq__' )         return  self.age == other.age          def  __ne__ (self,other ):         print('call __ne__' )         return  self.age != other.age          def  __gt__ (self,other ):         print('call __gt__' )         return  self.age > other.age          def  __ge__ (self,other ):         print('call __ge__' )         return  self.age >= other.age 
call __gt__
True
call __lt__
False
call __eq__
True
如上,只要一个实例变量实现了__lt__这样的方法,那实例也可以进行比较。
bool函数 
False
True
True
False
True
False
从上边的输出可知,对空列表、空字符调用bool函数时返回False,对有元素的列表、字符调用bool函数时返回True。这其内部是什么原理呢?先来看一个类,如下:
1 2 3 4 5 6 7 class  Grok :    def  __init__ (self,val ):         self.val = val              def  __bool__ (self ):         print('call __bool__' )         return  not  self.val 
call __bool__
True
call __bool__
False
从上边的例子可知,在调用bool(object)函数时其实是调用了__bool__这个方法。
接下来再一个例子,如下:
1 2 3 4 5 6 7 class  Seq :    def  __init__ (self,*args ):         self.val = args              def  __len__ (self ):         print('call __len__' )         return  len(self.val) 
call __len__
3
从上边的输出可知执行len(s1)时,实质是执行了__len__函数。
call __len__
0
call __len__
True
call __len__
False
从上边的输出可知,当执行bool(object)函数时也是调用了__len__函数,但是把结果进行了bool计算。如果__len__和__bool__方法同时存在类中时,当我们执行bool(object)时是调用哪个方法呢?做如下测试:
1 2 3 4 5 6 7 8 9 10 11 class  Seq_1 :    def  __init__ (self,*args ):         self.val = args              def  __len__ (self ):         print('call __len__' )         return  len(self.val)          def  __bool__ (self ):         print('call __bool__' )         return  True  
call __bool__
True
上边实例化Seq_1类时传递了一个空序列,但调用bool函数时返回了True,这表明,当类中实现了__bool__方法时会被调用,而__len__方法则不会被调用,如果没有__bool__时才调用__len__方法。
hash()与可hash对象 
1 2 3 4 5 6 class  Hash :    def  __init__ (self,val ):         self.val = val              def  __hash__ (self ):         return  123  
123
从上边输出可知,执行hash(object)函数时,实质是调用了__hash__方法,如果类中不定义__hash__方法呢?如下:
1 2 3 class  Hash_1 :    def  __init__ (self,val ):         self.val = val 
8795459756152
依然得到了一个hash值,为什么呢?这是因为python中所有的类都继承了object基类,object类已实现了__hash__方法,可以用dir(object)查看object这个类的属性。
可调用对象 
在python中可以用callable函数查看一个对象是不是可调用,如下:
1 2 def  fn ():    print('ha ha ha' ) 
True
输出为True,函数当然是一个可调用对象,如果是一个类呢?如下:
1 2 3 class  Fun :    def  __init__ (self,name ):         self.name = name 
False
如上,一个类被实例化后,这个实例对象是一个不可调用对象。
有什么方法能让一个实例对象变成可调用对象呢?做如下修改:
1 2 3 4 5 6 class  Fun_1 :    def  __init__ (self,name ):         self.name = name              def  __call__ (self ):         print('my name is {0}' .format(self.name)) 
True
cool,f1这个实例现在已是一个可调用对象了。所以只要一个类中实现了__call__方法,那么类实例就是一个可调用对象。调用此对象试试,如下:
my name is zhaochj
如上,事实证明,调用此实例对象也就是执行了__call__方法。既然是可调用对象,就可以向调用函数一样传递参数来调用,只要在__call__方法中定义可接收参数即可。
类通过实现__call__方法可以让实例变成一个可调用对象,如果我们向这个可调用对象传递一个函数作为其参数,那__call__函数就可以写成一个装饰器,如下:
1 2 3 4 5 6 7 8 9 10 11 12 import  functoolsclass  InjectUser :    def  __init__ (self,default_user ):         self.user = default_user              def  __call__ (self,fn ):         @functools.wraps(fn)         def  wrap (*args,**kwargs ):             if  'user'  not  in  kwargs.keys():                 kwargs['user' ] = self.user             return  fn(*args,**kwargs)         return  wrap 
1 2 3 @InjectUser('zhaochj') def  do_somthings (*args,**kwargs ):    print(kwargs.get('user' )) 
zhaochj
分析一下上边的代码:
1 2 3 4 5 6 7 def __call__(self,fn):         @functools.wraps(fn)         def wrap(*args,**kwargs):             if 'user' not in kwargs.keys():                 kwargs['user'] = self.user             return fn(*args,**kwargs)         return wrap 
上边的代码是定义一个装饰器。
1 2 3 @InjectUser('zhaochj') def do_somthings(*args,**kwargs):     print(kwargs.get('user')) 
这里的魔法等价执行了InjectUser('zhaochj')(do_somthings),实质是返回装饰器的wrap函数。执行do_somthings()时,实质是执行了wrap(),并返回fn(*args,**kwargs),到这里才真正执行
1 2 def do_somthings(*args,**kwargs):     print(kwargs.get('user')) 
函数中的print语句。
所谓单例,是指一个类的实例从始至终只能被创建一次。单例的实现有多种,这里以__call__方法来实现。
1 2 3 4 5 6 7 8 9 10 class  Single :    __instance = None           def  __init__ (self,cls ):         self.cls = cls              def  __call__ (self,*args,**kwargs ):         if  self.__instance is  None :             self.__instance = self.cls(*args,**kwargs)         return  self.__instance 
1 2 3 @Single class  Grok1 :    pass  
140271956645592
140271956645592
True
现在来分析一下上边的代码:
1 2 3 4 5 @Single class Grok1:     pass grok1 = Grok1() 
实例化Grok1类时相当于执行了下边两步:
1 2 1. Grok1 = Single(Grok1) 2. grok1 = Grok1() 
第一步:执行Single(Grok1)是返回一个Single类的实例,并用一个变量Grok1指向这个实例对象,此时的Grok1不是class Grok1:里的Grok1类,只是名字相同而已,Single类实例化时__init__构建函数被调用,这里会把self.cls这个实例属性指向Grok1类。
第二步:因Single类实现了__call__方法,所以此实例是一个可调用对象,这里执行grok1 = Grok1(),其中的Grok1已不再是class Grok1:里的类,而是Single类的实例对象,这里调用实例对象就会执行__call__方法,对此方法中的代码做进一步分析
1 2 3 4 def __call__(self,*args,**kwargs):         if self.__instance is None:             self.__instance = self.cls(*args,**kwargs)         return self.__instance 
当第一次调用时,if语句的结果为True,此时会执行self.__instance = self.cls(*args,**kwargs),其中的self.cls指向Grok1类,即这里表示实例化Grok1类,并把self.__instance指向实例化Grok1类的对象,这样__instance就不再是None了,当第二次调用__call__函数时,if语句的结果为False,所以直接执行return self.__instance,所以Grok1这个对象在被多次实例化后指向的实例化对象都是一样的,其实是只被实例化了一次而已。