面向对象相关知识补充(一)

一、isinstance(obj, cls)

  检查obj是否是类cls的对象。

class Foo:
    pass

obj = Foo()

print(isinstance(obj,Foo))

二、issubclass(sub, super)

  检查sub类是否是 super 类的派生类

class Foo:
    pass

class Bar(Foo):
    pass

issubclass(Bar,Foo)

 三、__setattr__,__getattr__,__delattr__

  上述三个函数,具体如何使用,请看代码:

  __setattr__:对属性进行赋值或者修改的时候被调用。

class Foo:
    def __init__(self,name):
        self.name = name

    def __setattr__(self, key, value):
        # self.__dict__[key] = value
        print('我执行了')

f = Foo('alex')  # 凡是对属性赋值或者修改操作,都触发__setattr__的执行
f.x = 3 # 触发__setattr__执行
print(f.__dict__)

#######输出结果#############
我执行了
我执行了
{}

  从上面结果可以看出,当我们设置属性或者给属性赋值的时候,会调用__setattr__的执行。但为什么对象f的名称空间没有我们设置的name和x两个变量呢?原因是:因为你重写了__setattr__功能,对它并没有进行任何操作,仅仅只有一条语句,而默认的__setattr__会将属性按照键值对写入对象的名称空间。如果自己重写了__setattr__,但是又没有进行任何操作,要想完成对象属性的赋值操作,必须得通过以下方式才能实现:

f.__dict__['name'] = xiaohua

print(f.name)

##输出结果###
xiaohua

  所以,如果我们自己要重写__setattr__方法,必须按照如下方式来做:

    def __setattr__(self, key, value):
        self.__dict__[key] = value

  __getattr__:只有在调用属性时且属性不存在的情况下,触发這个函数的执行。

class Foo:
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):
        print('没有你要找的元素')

f = Foo('alex')
f.x = 3
print(f.__dict__)

#####输出结果######
{'name': 'alex', 'x': 3}  # 此时对象名称空间有两个属性值

  当我们重写了__getattr__方法后,并且试图调用一个不存在的属性值:

class Foo:
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):
        print('没有你要找的元素')

f = Foo('alex')
f.x = 3
print(f.__dict__)
f.a  # 调用一个不存在的属性值

########输出结果########
没有你要找的元素

  由结果分析,正如我们之前所说的,调用一个不存在的属性值时,会触发__setattr__的执行。所以,一般将getattr设置为如下样式:

    def __getattr__(self, item):
        return self.__dict__[item]

  __delattr__:当删除一个对象的属性值时,会触发delattr的执行。

class Foo:
    def __init__(self,name):
        self.name = name
    def __delattr__(self, item):
        print('我执行了')


f = Foo('alex')
f.x = 3
del f.name  # 触发delattr的执行

###输出结果######
我执行了

  一般重写为:

    def __delattr__(self, item):
        del self.__dict__[item]

四、__getitem__,__setitem__,__delitem__

  我们在列表中学过这种取元素的方式。比如说lst = [1,2,3,4],取第一个元素 lst[0],又或者在字典中dd ={'name':'xiaohua'} 取元素dd['name']。实际上,这种取元素的方式,与__getitem__,__setitem__,__delitem__三个函数有关。

  下面,我们一一来看看上述三个函数的用法:

  __getitem__:当我们想要按照 obj[attr]方式调用对象的属性时,触发这个函数的执行

class Foo:
    def __init__(self,name):
        self.name = name
    def __getitem__(self, item):
        print('我执行了')

f = Foo('alex')
print(f.__dict__) # 对象的名称空间只有一个属性
f['name']  # 触发getitem执行

#####输出结果########
{'name': 'alex'}
我执行了

  从上面结果,可以看出,当我试图以f['name']方式调用属性的时候,就会触发__getitem__执行。一般将__getitem__设置为如下:

 def __getitem__(self, item):
        return self.__dict__[item]

print(f['name'])
###正常输出######

  __setitem__:当我以f['name'] = 'xiaohua' 方式修改对象属性值的时候,会触发该函数的执行。

class Foo:
    def __init__(self,name):
        self.name = name

    def __setitem__(self, key, value):
        print('我执行了')

f = Foo('alex')
print(f.__dict__)
f['x'] = 3  # 触发setitem执行

#########输出结果#########
我执行了

  一般将setitem设置为如下,当然你也可以按照自己的方式进行设置。

class Foo:
    def __init__(self,name):
        self.name = name

    def __setitem__(self, key, value):
         self.__dict__[key] = value

f = Foo('alex')
print(f.__dict__)
f['x'] = 3
print(f.__dict__)

###输出结果#######
{'name': 'alex'}
{'name': 'alex', 'x': 3}

  __delitem__:当以del f['name'] 方式删除对象的属性值时,会触发這个函数的执行

class Foo:
    def __init__(self,name):
        self.name = name

    def __delitem__(self, key):
        print('我执行了')


f = Foo('alex')
print(f.__dict__)
del f['name']  # 触发delitem执行

####输出结果#####
{'name': 'alex'}
我执行了

  一般设置为如下方式:

class Foo:
    def __init__(self,name):
        self.name = name

    def __delitem__(self, key):
        del f.__dict__[key]


f = Foo('alex')
print(f.__dict__)
del f['name']
print(f.__dict__)

 五、__str__,__repr__

  先来一段代码,再来引入我们的正题:

lst = list([1,2,3,4])
class Foo:
    def __init__(self,name):
        self.name = name

f = Foo('xiaohua')

print(lst)
print(f)

######输出结果#########
[1, 2, 3, 4]
<__main__.Foo object at 0x0000005FE8BC9208>

  我们都知道在python里面,list是一种数据类型,而且也是一个类。当我们以list()来创建一个序列时,我们也就创建了一个List对象。如上面的lst,知道了lst是List类型的对象后,我们print(lst)时,为什么没有像f一样,打印出对象的内存地址,而是直接打印出给对象赋的值呢?其实,这一切是因为在list类的内部,实现了__str__功能所导致的。下面,我们通过一些实例,来理解__str__的用法。

class Foo:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return '我执行了'

f = Foo('xiaohua')
print(f)

#######输出结果############
我执行了

  看,当我再次打印对象的时候,没有打印出对象的内存地址,而是打印出了我们设定的值。__str__就是帮我们实现这种功能的!我们可以定制自己__str__,让他返回一些有意义的信息。

    def __str__(self):
        return 'name:%s' % self.name

  print(f)

  ####输出结果#####
  name:xiaohua

  如上所示,现在我们可以理解,为什么打印列表对象lst的时候,会返回[1,2,3,4]了吧。

  注意:__str__:只能返回一个字符串类型

  理解了__str__后,__repr__就很好理解了。它和__str__一样,也是在打印对象的时候,会触发這个函数的执行。

六、__new__,__del__,

  __new__:创建对象时被调用,比__init__先执行。

class Foo(object):
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new %s' %cls)
        return object.__new__(cls, *args, **kwargs)
Foo()

###输出结果#######
new <class '__main__.Foo'>  # 比init先打印
init

  从上面,我们可以总结出如下几点:   

    继承自object的新式类才有__new__

    __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

    __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

    __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

  若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行:

class A(object):
    pass

class B(A):
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new %s' % cls)
        return object.__new__(A, *args, **kwargs)
b = B()
print(type(b))

输出结果:
new <class '__main__.B'>
<class '__main__.A'>

  __del__:在对象被删除或者程序执行完毕后,被调用

# 以下代码执行完毕后,自动执行del
class B:
    def __del__(self):
        print('我执行了')
b = B()

######输出结果#########
我执行了
#显式调用del来删除对象时,触发__del__执行
import time
class B:
    def __del__(self):
        print('我执行了')
b = B()
del b # 触发__del__执行
time.sleep(5)
print('主程序结果')

###输出结果####
我执行了
#等待5秒
主程序结果

  

原文地址:https://www.cnblogs.com/vipchenwei/p/7019887.html