根据实际使用过程中的报错问题引出的Python的type和classobj的总结。
最近在调用super的过程中,报了一个很错误:
代码语言:txt复制TypeError: must be type, not classobj
对这个错误有点诧异,因为在Python2和Python3上运行结果不同,3就不会报错,一查才知道Python2中的类的定义分为两种,经典类(也就是报错中提到的classobj)和新式类,而Python中super只能应用于新式类,而不能应用于经典类。
经典类
所谓经典类就是什么都不用继承的类,例如最初的A类就是经典类,下面是一个经典类的例子:
代码语言:txt复制class A():
...
新式类
所谓新式类就是必须要有继承的类,如果什么都不想继承,就继承到object类。下面是一个新类的例子:
代码语言:txt复制class B(object):
...
由于Python2中新式类的定义必须显式继承object,否则会被认为是经典类,所以报错。而在Python3中,所有类都默认继承自object,也就是说Python3中全部都是新式类,没有经典类,所以也就不会报错。
进一步的思考,为啥报错中提到must be type?难道不是应该是object类型吗,object和type又是什么关系?先放结论:
object是所有类的超类。而type是什么呢?它是object的类型(也就是说object是type的实例),同时,object又是type的超类。总之来说,object和type是鸡生蛋,蛋生鸡,谁先生谁,无法说。
上面这段结论必须要围绕Python中一切皆是对象的理念来思考。对于所有类的超类object,它是被定义的类,但这个类也是对象,它的类型就是type,如果了解Python中元类编程大概就能明白这句话的意思了,这里我简单说一下大概:因为Python是动态语言,所以类的创建也是在程序运行过程中创建的,创建类的方式就是通过type函数(还有一种方式就是元类),type函数既可以返回实例的类型,也可以用于创建一个类,被创建的类的类型就是type。
定义一个类Hello
代码语言:txt复制class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
测试如下:
代码语言:txt复制from hello import Hello
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class 'hello.Hello'>
type可以直接用于创建一个类:
代码语言:txt复制def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
所以现在我们明白了,在Python2中显式继承自object的类,都是由type创建的,都是type类型,至于object,它是Python中所有类的超类。type和object是Python中两个源对象,二者的关系没有严格的父子关系,互相依赖对方来定义,所以它们不能分开而论。type和object的测试如下:
代码语言:txt复制>>> object #===>(1)
<class 'object'>
>>> type #===>(2)
<class 'type'>
>>> type(object) #===>(3)
<class 'type'>
>>> object.__class__ #===>(4)
<class 'type'>
>>> object.__bases__ #===>(5)
()
>>> type.__class__ #===>(6)
<class 'type'>
>>> type.__bases__ #===>(7)
(<class 'object'>,)
结论如下:
- (1),(2):Python中的两个源对象的名字。我们先前说过type()是用来获对象的类型的。事实上,它既是一个对象,也是获取其它对象的类型的方法。
- (3),(4):查看object的类型。看到object是type的实例,我们另外也用.class来核实它和type()的输出是一样的。
- (5):object没有超类,因为它本身就是所有对象的超类。
- (6),(7):分别输出type的类型和超类。即,object是type的超类。type的类型是它自己
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!