最近在学python的GUI编程,遇到一个问题。
报错内容: super(Application,self).__init__(master)
TypeError: must be type, not classobj
代码如下:
from Tkinter import *
#基于Frame框架的Application类
class Application(Frame):
def __init__(self,master):
super(Application,self).__init__(master)
self.grid()
root = Tk()
root.geometry("800x600")
app =Application(root)
#调用根窗口的时间循环,目的是启动GUI并保持其运行状态
root.mainloop()
查了资料发现的原因是:
早期的python继承体系中,对父类初始化并没有使用super(Application,self).__init__(master)这种方式,相对于其上例子,而是直接使用Frame.__init__(self,master)方式。
这是为什么?看Tkinter库的源代码,Frame类是继承Widget类,而Widget是继承类BaseWidget, Pack, Place,Grid,这四个类所继承的基类都有类似class Clazz: 这样的写法
在Python早期中以这样形式(super(Application,self).__init__(master))执行父类初始化工作是不存在的。stackoverflow上说:Old-style classes (also known as"classic" classes) are always of type classobj; new-styleclasses are of type type.
就是说早期类是classobj类型的,而新类是type类型的。而super()返回一个特殊对象,这个对象类型与以前的对象类型是不兼容的,所以才会报错。从这都可以看出来,Tkinter是多么的古老。
看看这两种构造初始化方法的区别:
1. Frame.__init__(self,master)。
2. super(Application,self).__init__(master)
像第一种方法,指明父类进行初始化,显示出对不同父类初始化,这种语法更加的明确,但是却是有问题的:
class Test0(object):
def __init__(self):
print("这是Test0的初始化方法")
class Test1(Test0):
def __init__(self):
Test0.__init__(self)
print("这是Test1的初始化方法")
class Test2(Test0):
def __init__(self):
Test0.__init__(self)
print("这是Test2的初始化方法")
class Test3(Test2,Test1):
def __init__(self):
Test2.__init__(self)
Test1.__init__(self)
test = Test3()
输出结果:
这是Test0的初始化方法
这是Test2的初始化方法
这是Test0的初始化方法
这是Test1的初始化方法
注意: 基类(Test0)进行了两次初始化,这就是多继承产生的问题,也就是钻石形状问题。
而super()这种方式正好解决了钻石形状问题,如下代码所示:
class Test0(object):
def __init__(self):
super(Test0,self).__init__()
print("这是Test0的初始化方法")
class Test1(Test0):
def __init__(self):
super(Test1,self).__init__()
print("这是Test1的初始化方法")
class Test2(Test0):
def __init__(self):
super(Test2,self).__init__()
print("这是Test2的初始化方法")
class Test3(Test2,Test1):
def __init__(self):
super(Test3,self).__init__()
test = Test3()
输出结果:
这是Test0的初始化方法
这是Test1的初始化方法
这是Test2的初始化方法
从上面的这个例子看出,在多继承的初始化过程中,会生成一个列表,这个列表里有该类继承的所有父类,但是每个类只会出现一次。所以解决了父类重复初始化的问题。
这个方法虽然解决了问题,但是,当继承多个类时,就无法对所有的父类统一进行初始化了,如下代码:
class Test0(object):print (test2.name0)
输出显示:
Traceback (most recent call last):
File "F:\new_android_workspace\firstpro\src\main\test4.py", line 18, in
实例
print (test2.name0)
AttributeError: 'Test2' object has no attribute 'name0'
以上代码使用super()初始化,只会默认执行继承列表中的第一个类的init方法,所以特性name是有值的,name0是没有值的。
在多继承的初始化过程中,生成一个列表时,这个列表里有该类继承的所有父类,当访问父类的成员时(例如__init__()方法),会从这个列表里进行查找,直到找到某个父类拥有这个成员为止,执行这种查找也有一个条件,即每一个init方法中必须要有一个super()方法“启动“列表中下一个类的init方法。
如下代码所示:
class Test0(object):
def __init__(self):
super(Test0,self).__init__()
print "执行了Test0的初始化"
class Test1(object):
def __init__(self):
#super(Test1,self).__init__()
print "执行了test1的初始化"
class Test2(Test1,Test0):
def __init__(self):
super(Test2,self).__init__()
test2 = Test2()
输出显示:
#执行了Test0的初始化
执行了test1的初始化
注意: 如果隐藏了绿色的这行代码,那么输出结果中的绿色代码也不会显示了。
如下代码:
class Test0(object):
def __init__(self,name0,name1):
self.name0 = name0
self.name1 = name1
class Test1(object):
def __init__(self,name):
self.name = name
class Test2(Test1,Test0):
def __init__(self,name1,name2):
super(Test2,self).__init__(name1,name2)
test2 = Test2("name0","name2")
print test2.name0
print test2.name1
输出显示:
Traceback (most recent call last):
File "F:\new_android_workspace\firstpro\src\main\test4.py", line 18, in
test2 = Test2("name0","name2")
File "F:\new_android_workspace\firstpro\src\main\test4.py", line 17, in __init__
super(Test2,self).__init__(master,master1)
TypeError: __init__() takes exactly 2 arguments (3 given)
错误提示表明test2= Test2("name0","name2")语句只调用了Test1的init方法,想通过类似java的方法重载来实现调用相应的父类初始化是不可行的。
如果你看到Test1中缺少了super语句,确实,在python的多继承体系中,super方法在以上所说的生成的父类列表中还充当连接下一个类的角色,但是,如果在Test1中加入super语句,那么该super语句,将需要严格按照列表中下一个类的参数进行匹配。也就是如下所示的更改:
class Test0(object):
def __init__(self,name0,name1):
self.name0 = name0
self.name1 = name1
class Test1(object):
def __init__(self,name1,name2):
super(Test1,self).__init__(name1,name2)
class Test2(Test1,Test0):
def __init__(self,name1,name2):
super(Test2,self).__init__(name1,name2)
test2 = Test2("name0","name1")
print test2.name0
print test2.name1
输出显示:
name0
name1
如果是这样就执行了初始化,但感觉还是有点别扭,不过使用单继承会好一些。