python类中的初始化问题

来源:互联网 发布:傻瓜p图软件 编辑:程序博客网 时间:2024/05/02 04:45

最近在学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):
    def __init__(self,name0):
        self.name0 = name0
class Test1(object):
    def __init__(self,name):
        self.name = name
class Test2(Test1,Test0):
    def __init__(self,master):
        super(Test2,self).__init__(master)
test2 = Test2("name2")
print (test2.name)

print (test2.name0)

输出显示:

Traceback (most recent call last):
  File "F:\new_android_workspace\firstpro\src\main\test4.py", line 18, in <module>
实例
    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 <module>
    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

如果是这样就执行了初始化,但感觉还是有点别扭,不过使用单继承会好一些。

0 0
原创粉丝点击