类与对象深度
一、抽象基类
日常运用中,判断一个类内部是否含有某个方法,我们可以使用hasattr()方法,即:
hasattr([类名],"[方法名]")
这里我们可以通过使用isinstance()通过判断该类是否为某个抽象基类的子类来判断,该类是否含有某抽象基类所特有的方法。 另外,抽象基类在对强制子类重写方法方面有着比寻常方法更为快捷的优势,这里,我们可以举个例子来说明一下,具体有哪方面的优势。
例:
定义父类Cache,封装CRUD方法,强制子类重写方法,定义子类Redis。
这里,我们可以对比一下以下两种方法。
方法1:
class Cache(object):
def CRUD(self):
raise NotImplementedError
class Radis(Cache):
pass
a=Radis()
a.CRUD()
运行结果:
D:\python3.7.6\python.exe E:/flask学习/fuxi.py
Traceback (most recent call last):
File "E:/flask学习/fuxi.py", line 7, in <module>
a.CRUD()
File "E:/flask学习/fuxi.py", line 3, in CRUD
raise NotImplementedError
NotImplementedError
Process finished with exit code 1
在这个方法中,我们使用了父类主动抛出异常的方式来强制子类进行方法的重写,否则就会报错。
方法二:
import abc
class Cache(metaclass=abc.ABCMeta):
@abc.abstractmethod
def CRUD(self):
pass
class Redis(Cache):
pass
a=Redis()
运行结果:
D:\python3.7.6\python.exe E:/flask学习/fuxi.py
Traceback (most recent call last):
File "E:/flask学习/fuxi.py", line 8, in <module>
a=Redis()
TypeError: Can't instantiate abstract class Redis with abstract methods CRUD
Process finished with exit code 1
两个结果都是成功地抛出了异常,但是当我们去对比抛出异常的具体执行程序的时候则不难发现,运用第一种方法抛出的异常出现在了访问该方法的时候,这个对象成功地被实例化了出来,但用第二种方法抛出的异常则是出现在了将对象实例化地过程中,在我们日常编写代码的过程中,第二种方法无疑是最方便程序员在代码的测试中发现问题所在,可以很大程度上提高我们代码的编写、纠错效率。
二、对象类型判断
isinstance()与type()
共同点:
isinstane()与type()都是可以判断某个对象的所属类型
不同点:
isinstance(a,b)返回布尔值,重点在于判断a是否为b类的所属类型,其中包括了a的类可以是b的子类
type(a)返回值为类型,比如a=3,那么返回的type(a)则是int。另外,我们使用is来判断b是否是a的类型时,并不包括b是a的子类。
三、属性
对象可以向上查找,即可以访问到类属性,类不能向下查找,即类不能访问到实例属性。
注意
当对象有实例属性时,直接输出实例属性,没有时才会向上查询到类属性中。即我们在使用对象修改该对象中类属性的值时,并未真的修改
类属性值,只是相当于将所写的属性值封装进了该对象之中,类属性值没有变。
关于多继承查询,在python2.2版本前,我们使用的是DFS(深度优先算法),在2.2以后,我们常用BFS(广度优先算法),另外,在面对复杂继承结构时,如果我们不确定查询顺序,我们可以使用__mro__直接查看继承顺序。
四、继承调用
若想要在子类中调用父类属性,我们可以使用下列两种方法。
方法一:直接通过父类的类名调用,当然,调用时需注意注明父类的self属性
方法二:使用super()方法调用父类属性
上述两种方法中,我们通常选用第二中编程方法,我们称之为软编码,原因在于,这个方法中,父类的类名可以随意更改,子类不受影响。