Python 面向对象基础(四)--特殊方法

特殊方法的定义:

1.定义在某些class当中

2.不需要直接调用

3.Python的某些函数或者是操作符会调用相应的特殊方法

特殊方法很多,我们只需要编写用到的特殊方法,以及有关联性的特殊方法。

 

  • __init__(self[, ...])     构造方法

  • __new__(cls[, ...])  

  • __del__(self)         析构方法

 __init__  构造器,当一个实例被创建的时候初始化的方法。但是它并不是实例化调用的第一个方法,__new__才是实例化对象调用的第一个方法,它只取下 cls参数,并把其他参数传给 __init__。 __new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。

class Foo(object):
    def __init__(self):
  '''实例化时构造方法执行''
print('run') F=Foo() -------- run

 __new__ 

  1. __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
  2. __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
  3. __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值 # 若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行
class A(object):                                                                                                
    '''当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。'''                                        
    def __init__(self,name):                                                                                    
        self.name=name                                                                                          
        print("init")                                                                                           
                                                                                                                
    def __new__(cls, *args, **kwargs):  # 必须有cls参数                                                              
        '''重新构造__new__方法'''                                                                                     
        print('__new__  is  runing:(')                                                                          
        return object.__new__(cls)   #必须return 基类的__new__方法才能调用__init__继续实例化属性                                  
                                                                                                                
a=A('name')                                                                                                     
print(a.name)    
                                                                                               
--------------
__new__  is  runing:(
init
name

 __del__ 

__del__ 析构器,当实例被销毁时调用

#__del__析构函数
class Animal(object):
    def __init__(self,name):
        self.name=name
    def eat(self):
        print('eating……')
    def __del__(self):
        '''析构函数
        会自动执行'''
        print("析构执行")


cat=Animal('kim')
cat.eat()
del cat#手动摧毁对象  执行__del___

 __call__ 

__call__ 允许一个类的实例像函数一样被调用

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Animal(object):
    def __init__(self):
        pass
    def __call__(self,value):
        print('__call__ 执行 %s'%value)

A=Animal()
A('call')

 __doc__

__doc__输出:类的描述信息
class Foo:
    """ 描述类信息,这是用于看片的神奇 """

    def func(self):
        pass


print(Foo.__doc__)
结果:
描述类信息,这是用于看片的神奇

__dict__

查看类或对象中的所有成员 

class Province:
 
    country = 'China'
 
    def __init__(self, name, count):
        self.name = name
        self.count = count
 
    def func(self, *args, **kwargs):
        print 'func'
 
# 获取类的成员,即:静态字段、方法、
print Province.__dict__
# 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
 
obj1 = Province('HeBei',10000)
print obj1.__dict__
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'}

 __str__

如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

class Foo:
 
    def __str__(self):
        return 'alex li'
 
 
obj = Foo()
print obj
# 输出:alex li

  __module__ 和  __class__ 

  __module__ 表示当前操作的对象在那个模块

  __class__     表示当前操作的对象的类是什么

#lib/aa.py
class C:
    def __init__(self):
        self.name = 'wupeiqi'

 

from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

 

  • __getitem__(self,key)

  • __setitem__(self,key,value)

  • __delitem__(self,key)

用于索引操作,如字典。以上分别表示获取、设置、删除数据

class Foo(object):
 
    def __getitem__(self, key):
        print('__getitem__',key)
 
    def __setitem__(self, key, value):
        print('__setitem__',key,value)
 
    def __delitem__(self, key):
        print('__delitem__',key)
 
 
obj = Foo()
 
result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'alex'   # 自动触发执行 __setitem__
del obj['k1']   
  • __getattr__(self,name)

  __getattr__ 定义当用户试图访问一个不存在属性的时候的行为

class ClassA(object):
    '''当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。 '''
    def __init__(self, classname):
        self.classname = classname


    def __getattr__(self, attr):
return('invoke __getattr__', attr) insA = ClassA('ClassB') print(insA.__dict__) # 实例insA已经有classname属性了 # {'classname': 'ClassB'} print(insA.classname) # 不会调用__getattr__ # ClassA print(insA.grade) # grade属性没有找到,调用__getattr__ # ('invoke __getattr__', 'grade')
  • __getattribute__(self,name)

  __getattribute__ 定义当一个属性被访问的时候的行为

  无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


    def __getattr__(self, item):
        print("don't have the attribute ", item)
        return False

    def __setattr__(self, key, value):
        self.__dict__[key]=value  #这里等介于访问了属性。会调用__getattribute__

    def __getattribute__(self, item):
        print('__getattribute__ is running')
        # 注意此处不要用 self.__dict__[item]
        # 因为self.__dict__依然会被__getattribute__拦截 这样就会陷入循环
        return object.__getattribute__(self, item)


a = Person('p1', 20)
a.hh#先调用__getattribute__再内部继承基类的__getattribute__方法再调用__getattr__

结果:
__getattribute__ is running
__getattribute__ is running
__getattribute__ is running
don't have the attribute  hh
  • __setattr__(self,name,value)

  __setattr__ 定义当一个属性被设置的时候的行为

  注意:创建实例化类的时候会触发

  注意:重载了__setattr__ 所以 实例化的时候 __dict__ 被清空

class ClassA(object):

    def __init__(self, classname):
        self.classname = classname#属性设置触发__setattr__

    def __getattr__(self, attr):
        '''当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。 '''
        return ('invoke __getattr__', attr)

    def __setattr__(self, key, value):
        '''定义当一个属性被设置的时候的行为 '''
        print('invoke __setattr__', value)

insA = ClassA('ClassB')  #这里也设置属性的行为
#结果:invoke __setattr__ ClassB
insA.name='Ronny'       #这里也设置属性的行为
#结果:invoke __setattr__ Ronny
  •  __delatrr__(self,name)
  • 注意:创建实例化类的时候会触发
class A(object):
    def __init__(self,name):
        self.name=name
    def __delattr__(self, *args, **kwargs):
        print ('call func del attr'  )
        #return object.__delattr__(self, *args, **kwargs)
    def __del__(self):
        '''程序结算会执行'''
        print('call is __del__')
a=A('Ronny')
del a.name

结果:
call func del attr
call is __del__
  • __get__(self,instance,owner)

  • __set__(self,instance,value)

  • __delete__(self,instance)

什么是描述符类?

根据鸭子模型理论,只要具有__get__方法的类就是描述符类。
如果一个类中具有__get____set__两个方法,那么就是数据描述符,。
如果一个类中只有__get__方法,那么是非数据描述符。

__get__:当我们用类或者实例来调用该属性时,Python会返回__get__函数的结果。
__set__:当我们用实例来设置属性值时,Python会调用该函数。对类没有限制作用。
__delete__:当我们用实例试图删除该属性时,Python会调用该函数。对类没有限制作用。

非数据描述类

class Desc:
    def __init__(self, value=22):
        self.value= value
    def __get__(self, ins, cls):
        return self.value

class A:
    v=Desc()
a=A()
print(a.v)#由于实例中没有v属性,所以找到了类的属性,而类的属性是一个描述符类实例,所以调用其__get__方法的结果。
#结果:22
a.v=50#通过实例设置v属性,发现成功了。
print(a.v)
A.v=100
print(A.v)
print(a.__dict__) #我们发现实例的__dict__中存入了我们刚才设置的属性
print(A.__dict__)#类的__dict__没有发生任何变化

 

通过上面的测试,我们发现非数据描述类有如下特点:

  • 如果实例__dict__没有设置同名属性,那么返回描述类的__get__方法的结果。
  • 如果实例__dict__中存在同名属性,那么返回实例__dict__中的内容。
  • 对我们设置实例的__dict__中的行为并不做阻止。所以我说这是查看级别的描述类。

数据描述类

class Desc:
    def __init__(self, value=22):
        self.value= value
    def __get__(self, ins, cls):
        print('__get__ is ok')
        return self.value

    def __set__(slef, ins, value):
        slef.value=value
        print('__set__ is ok')

class A:
    v = Desc()
a=A()
a.v=50
print(a.v)#调用desc类的__get__
print(a.__dict__)#我们设置a.v后,发现实例的__dict__中仍然空空如也。因为此时调用的是__set__方法,值50存入到了Desc实例的value属性上了。
#结果:{}
A.v = 30#通过类,仍然可以改变属性
print(A.__dict__)#改变后,变成了普通属性了,这时甚至都已经不再是描述符类了。
#结果
{'__module__': '__main__', 'v': 100, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

说明如下:

  • __set__方法存在后,实例设置同名属性时,完全需要看__set__的脸色。
  • 如果描述类中__set__方法存在但是__delete__方法不存在,那么不能删除客户类中的属性。
  • 即使在__set__方法中做了限制,这个限制只是对实例而言的,对类没有起到作用。

  

 

posted @ 2017-09-21 18:05  Ronny_bin  阅读(97)  评论(0)    收藏  举报