在代码调试的过程中,报错如下:
TypeError: init() takes exactly 1 argument (2 given) 引发的原理分析
以下的分析问题的过程
查看代码
from abc import abstractmethod,ABCMeta
class Rootfunction(object):
__metaclass = ABCMeta
def __init__(self,driver=None):
self.driver = driver
@abstractmethod
def __init__(self):
pass
def get_method(self,name):
return self.driver.get(name)
def get_all_func(self):
return self.driver.get("all_function")
可以看到,写了一个抽象类,抽象类就是类的集成,换言之是一组具有相似性的类,包括数据属性和函数属性,而接口只强调函数属性的相似性。
普通类可以实例化和继承,而抽象类不能实例化,只能子类继承,然后子类去实现继承的方法,除非子类也是抽象类。
接口是对动作的抽象
抽象类是对本质的抽象
举个简单的例子
"""
抽象类
"""
import abc # 利用abc模块实现抽象类
# 一切皆文件
class All_file(metaclass=abc.ABCMeta):
all_type = 'file'
@abc.abstractmethod # 定义抽象方法,无需实现功能
def read(self):
'子类必须定义读功能'
pass
@abc.abstractmethod # 定义抽象方法,无需实现功能
def write(self):
'''子类必须定义写功能'''
pass
class Txt(All_file): # 子类继承抽象类,但是必须定义read和write方法
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的写入方法')
class Sata(All_file): # 子类继承抽象类,但是必须定义read和write方法
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的写入方法')
class Process(All_file): # 子类继承抽象类,但是必须定义read和write方法
def read(self):
print('进程数据的读取方法')
def write(self):
print('进程数据的写入方法')
wenbenwenjian = Txt()
yingpanwenjian = Sata()
jinchengwenjian = Process()
# 这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()
# 这样大家都是被归一化了,也就是一切皆文件的思想
print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
回到解决问题上面,如下图,第一个@abstractmethod是报错的代码,第二个@abstractmethod和第三个@abstractmethod都是经常修改都是可以正常运行的代码
from abc import abstractmethod,ABCMeta
class Rootfunction(object):
__metaclass = ABCMeta
def __init__(self,driver=None):
self.driver = driver
@abstractmethod # 1
def __init__(self):
pass
# @abstractmethod # 2
# def init(self):
# pass
# @abstractmethod # 3
# def __init__(self,drive=None):
# pass
def get_method(self,name):
return self.driver.get(name)
def get_all_func(self):
return self.driver.get("all_function")
那么问题来了,为什么第二个@abstractmethod和第三个@abstractmethod都可以正常运行呢?
首先看一下子类怎么写的
由图可知,第三个@abstractmethod是改写了父类的初始化方法__init__,虽然可以正常运行,但是会影响后续的性能。见下图父类
为什么第二个是合适的呢,让我们来重新梳理一下整个代码逻辑
在子类TestClass中,初始化方法里用了super().init,这个是用来做什么的呢?
class Root(object):
def __init__(self):
self.x = '属性'
def fun(self):
print(self.x)
print('方法')
class A(Root):
def __init__(self):
super(A, self).__init__()
print('实例化执行')
test = A() # 实例化类
test.fun() # 调用方法
test.x # 调用属性
执行结果:
实例化执行
属性
方法
如果不加super().init()会执行出什么
class Root(object):
def __init__(self):
self.x = '属性'
def fun(self):
print('方法')
class A(Root):
def __init__(self):
print('实例化执行')
test = Root() # 实例化类
test.fun() # 调用方法
test.x # 调用属性
执行如下:
方法
可以看到此时父类的方法继承成功,可以使用,但是父类的属性却未继承,并不能用
再试一种方法
class Root(object):
def __init__(self):
self.x = '属性'
def fun(self):
print('self.x')
print('方法')
class A(Root):
def __init__(self):
print('实例化时执行')
test = A() # 实例化方法
test.fun() # 调用方法
test.x # 调用属性
执行结果如下:
实例化时执行
self.x
方法
Traceback (most recent call last):
File "E:/code/September/025.py", line 47, in <module>
test.x # 调用属性
AttributeError: 'A' object has no attribute 'x'
Process finished with exit code 1
这样也可以看出,还是不能调用父类的属性
经过三次测试,可以看出,super().__init__是用来执行父类的构造函数,使得我们能够调用父类的属性。
(还有一种Root.init(self),在继承时会跳过重复继承,节省了资源,后面遇到再进行专门分析)
回到正题,看下图
在__init__方法里,子类调用父类的属性
在init方法里,子类对抽象类的父类进行继承重写
用第一种@abstractmethod和第三种@abstractmethod会将父类的__init__方法覆盖掉,第三种虽然可以运行,但在业务逻辑上是有问题的。
from abc import abstractmethod,ABCMeta
class Rootfunction(object):
__metaclass = ABCMeta
def __init__(self,driver=None):
self.driver = driver
@abstractmethod # 1
def __init__(self):
pass
# @abstractmethod # 2
# def init(self):
# pass
# @abstractmethod # 3
# def __init__(self,drive=None):
# pass
def get_method(self,name):
return self.driver.get(name)
def get_all_func(self):
return self.driver.get("all_function")
用第二种```python
from abc import abstractmethod,ABCMeta
class Rootfunction(object):
__metaclass = ABCMeta
def __init__(self,driver=None):
self.driver = driver
@abstractmethod # 1
def __init__(self):
pass
# @abstractmethod # 2
# def init(self):
# pass
# @abstractmethod # 3
# def __init__(self,drive=None):
# pass
def get_method(self,name):
return self.driver.get(name)
def get_all_func(self):
return self.driver.get("all_function")
即用第二种问题解决。