Python中的type和classobj

2023-11-21 18:54:15 浏览数 (1)

根据实际使用过程中的报错问题引出的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腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞