Warning:本篇为基础学习,由于笔者也在学习中,所以文章过于啰嗦,想直接了解type 函数的用法,推荐“IT技术随笔”的另一篇比较简洁直观的文章:https://www.cnblogs.com/wushuaishuai/p/7739728.html
当我们拿到一个变量(对象的引用)时,如何知道这个对象是什么类型,有哪些方法呢?
基本上入门级最开始使用的就是type 函数,针对于刚学习的小小白,我们在根据上文举几个例子,如:
1 num1 = 123
2 str1 = 'hello'
3 noneobj =None4 print(type(num1))5 print(type(str1))6 print(type(noneobj))7
8
9
10
当然,我们不使用变量,直接对数据对象本身进行type ,如type(123),type('hello'),type(None),结果是一样的。
对于一些内置变量,比如abs 函数,max 函数, len 函数,也都可以使用type 去获取他们的对象类型
1 print(type(abs))2 print(type(max))3 print(type(len))4
5 #这一类说明是Python 内置的函数
6
7
再来看看另外这几种情况,现有另外一个模块demo01.py,里面含有一个自定义函数print01 ,如下使用type 函数:
1 importtime2 from copy importdeepcopy3 importdemo014 deffn():5 pass
6 print(type(deepcopy))7 print(type(fn))8 print(type(time))9 print(type(demo01))10 print(type(demo01.print01))11 print(type(lambdax: x))12
13
14
15
16
17
18
19
由上面测试结果,我们可以分析得知,无论是我们自定义的模块demo01,还是Python 内置的模块time,使用type 函数,得到的结果,都"属于" module 这一类,
而函数方面,无论是我们在demo01.py 里面自定义的print01,或者自定义的匿名函数,还是Python 内置的函数deepcopy ,使用type 函数,得到的结果,都是"属于" function 这一类。
另外一种是我们自己定义的类(在demo02.py 中自定义一个Student 类),如下:
1 classTeacher(object):2 def __init__(self, name, age):3 self.name =name4 self.age =age5
6 if __name__ == '__main__':7 t1 = Teacher('Tom', 22)8 print(type(t1))9 from demo02 importStudent10 s1 = Student('Jam', 11)11 print(type(s1))12
13
14
15
这个就没什么说的了,只是介绍一种情况,分别是两个实例对象所属的类,一个是当前模块"__main__"的Teacher 类,另一个是引用模块"demo02" 的Student 类。
最后还有一种特殊的情况,比如int,list,set 等这些Python 的强转类型函数,或者是自定义的类,又或者是object 这个Python3 所有类的基类,又或者是type 这个函数本身呢,比如int,list,set 这些内置的数据类型类,或者是自定义的一个类,又或者是object 这个Python3 所有类的基类,又或者是type 这个类(注意,这个type 类和type 函数不是同一个"东西"),他们的对象类型是什么呢?
1 classA(object):2 pass
3
4 print(type(A))5 print(type(int))6 print(type(list))7 print(type(set))8 print(type(object))9 print(type(type))10
11
12
13
14
15
16
由此可知,这些都"属于" type 这一类,那么type 这一类是什么呢?请看扩展2
type 函数返回值
根据参数传入的不同,返回不同的结果,上文中我们运用到的是传入一个参数,返回值是这个参数的对象类型。
至于原理,网上很多的资料,都说都是调用了这个对象的__class__方法,我目前只知道他们的结果确实是一样的,源码中也并没有说明,也可能是我没看懂,后续如果学到了,或者有知道的朋友可以在下方评论留言,谢谢。
1 ...2 type(object_or_name, bases, dict)3 type(object) -> the object's type
4 type(name, bases, dict) ->a new type5 ...
乌龙事件:说好的万物皆是对象呢?整数"123"没有__class__魔法方法???
测试中产生一个疑问,对于Python 来说,万物皆是对象,但是当我们去调用一个整数数值的__class__方法时,会被报语法错误,难道整数数值就不是对象了?个人认为可能是解释器认为"."后面的应该是小数,那该怎么解决?(经过大佬的提醒,已解决,这么偏门也会想到,毕竟这门语言的年龄跟我一般大,暴露年龄)
一度怀疑这是个我新发现的BUG, 因为测试别的类型都没问题
1 a = 123
2 print(a.__class__)3 #print(123.__class__)
4 print(123.11.__class__)5 print((1,).__class__)6 print(None.__class__)7
8
9
10
11
12
13
一度兴奋到要给龟叔发邮件什么的,但是经过大佬一提醒:加个括号!想起来元祖中只有一个元素的时候,要加“,” 的原因
1 print((123).__class__)2
3
4
5
好吧,龟叔牛X(破音),大佬牛X(破音),咳咳,跑题了,不过经过上面的测试也明白,这个type 函数,可能就是调用对象的"__class__" 方法(懂了后就把"可能"去掉)
言归正传,计算机的所做,大致就是对于一件事情,进行判断,然后根据给的条件,执行相对应的操作,所以平常我们码代码的时候,if 判断用的肯定是比较多的,那么怎么对type 函数的返回值进行if 判断呢?
从最简单的,比如说一个变量,引用的是一个整数,但是我们不确定,那么该做什么判断呢?
1 a = 123
2 if type(a) ==int:3 print('a is int')4 else:5 print('a is not int')6
7
8
9 a is int
小数,字符串,列表等等 都可以直接去进行类似的判断,那么另外一种情况呢,假如有一个变量名fn, 我们不确定他是否是一个函数名,该怎么判断呢?
Python 内置的types 模块提供了三个常量量,供我们去判断一个对象是不是函数对象,分别是:FunctionType,BuiltinFunctionType,LambdaType
FunctionType
对于一些我们自己定义函数,或者是在别的模块中导入的函数(无论是我们自己的"demo02" 模块,还是Python 的"copy" 模块),他们判断时所对应的常量都是FunctionType
1 importtypes2 importdemo023 from copy importdeepcopy4
5 deffunc():6 pass
7
8 print(type(func) ==types.FunctionType)9 print(type(demo02.fn) ==types.FunctionType)10 print(type(deepcopy) ==types.FunctionType)11
12 True13 True14 True
BuiltinFunctionType
而对于一些像"abs", "max", "min" 的函数,他们对应的常量 都是BuiltinFunctionType,事实上还有一个BuiltinMethodType 常量名,只不过是BuiltinFunctionType 另一个别名罢了,不知道是用做什么,但我们最好还是使用BuiltinFunctionType,这样可以达到见字知意。
print(type(abs) ==types.BuiltinFunctionType)print(type(max) ==types.BuiltinMethodType)print(type(min) ==types.BuiltinFunctionType)print(type(min) ==types.BuiltinMethodType)
True
True
True
True
LambdaType
最后再来看看LambdaType ,从字面上看就知道这是个关于匿名函数的常量名,看下面的小例子:
1 f_la = lambdax: x2
3 print(type(f_la) ==types.FunctionType)4 print(type(f_la) ==types.LambdaType)5
6 True7 True
由上面的结果可以得出,匿名函数既是FunctionType 类型的,又是 LambdaType 类型的,这个本来就没问题,匿名函数本来就是函数的一个分支嘛~
说完了一个参数的,我们再来说说另外一个至少是我比较少用的用法,传入三个参数
光标在type 函数后面的括号里面按住"ctrl + P"
需要传入一个"str" 类型的name,类 的名称,
需要传入一个"tuple" 类型的bases,前面传入name 实参的基类的元祖,考虑多继承
需要传入一个"dict" 类型的dict,为一个字典,包含类定义内的命名空间变量,看下面的例子
1 classPeople(object):2 country = 'China'
3
4 t1 = type('Teacher', (People,), {'name': 'xiaoming', 'age': 30})5 print(t1.name)6 print(t1.age)7 print(t1.country)8
9
10 xiaoming11 30
12 China
扩展
1.说完这些后,我们再看看IDE 给我们报的警告,以最后一个LambdaType 的例子为例
大致意思就是不建议使用"==" 对类型进行比较,推荐使用isinstance 函数,那么使用isinstance 函数,有什么好处呢?或者说对比type 函数,进行判断的时候,有什么不同呢?
我先直接说出答案,然后再具体举例子说明:
type 函数不认为子类是父类类型的一种,即不考虑继承关系
isinstance 函数,认为字类是父类类型的一种,即考虑继承关系
所以如果判断两个对象类型是否相等,PEP8 规范推荐使用isinstance 函数,具体代码如下:
classPeople(object):pass
classTeacher(People):pass
if __name__ == '__main__':
p1=People()
t1=Teacher()print(isinstance(t1, People))print(type(t1) ==People)print(isinstance(t1, Teacher))print(type(t1) ==Teacher)
True
False
True
True
2.相信很多初学者都跟我一样很好奇,type、object、class 的关系(注意:这里所说的type ,并不是上文介绍的type 函数,而是一个类),先看下面这个图
如果抛开type 这个类,我们很多初学者都明白其中的关系:
①.object 是所有类的基类,比如list,str,dict,tuple 等等,都是继承object 类
②.字符串"abc" 是str 类的一个实例对象
那么让我们来想想Python 的口号:在Python 中,万物都是对象!那么也就是说,list,str,dict,tuple,甚至是object 这些类,也都是实例对象了,那么问题来了,谁是他们的类呢?绕口点说就是谁是Python 中所有类的类呢?没错,就是type 了,这个神奇的类,是所有Python 中类的类,哈哈,被绕晕了吗?请看下面我写的一个伪代码,注意,是伪代码,仅做学习了解用:
1 classtype(object):2 pass
3
4 int =type() # int 为Python 中的整型类5 list =type() # list 为Python 中的列表类6 str =type() # str 为Python 中的字符串类7 ...8 #当你自定义了一个类,比如Teacher ,那么肯定会有下面这一步伪代码的
9 Teacher =type()10
这样虽然不怎么合适,但我感觉还算比较好理解他们的关系,如果有更好的解释方法,欢迎在下面留言~
也就是说,当你创建一个字符串对象,其实在Python 中的步骤是先由type 这个类,实例化一个对象str 类,然后再由str 类去实例化一个字符串对象,如:"hello world",其他类型也是同样的步骤。
那么它又是谁的实例对象呢?看图应该可以看出来,它是它自己的实例对象。是不是感觉很不可思议?这就是Python 的神奇之处了,可能别的语言也又类似的设计,不甚了解。
type = type()
在这里再做一个小扩展,之前刚接触object 的时候,在了解到它是所有类的基类的时候,就曾经好奇过,它的父类是谁?学习完这个type 类,见识了Python 中的这种操作后,我想,object 一定还是继承他自己吧,毕竟type 类都能实例化它自己,如果你想的和我一样的话,那么只能说很遗憾,我们都错了。。
这里介绍两个魔法方法,__class__ 和__bases__
在上面的乌龙事件(说好的万物皆对象呢?整数"123"没有__class__魔法方法??)中已经说了,Python 万物皆是对象,那么所有的对象都是经过类实例化出来的,那么所有的对象都会由__class__这个方法,调用该方法,能查看到该对象是由谁实例化出来的,例子往上看吧
这里重点说以下__bases__ 魔法方法,也就是验证object 类的父类是自身还是别的类的一种魔法方法,从字面意思上看,是查看一个对象的基类,事实上也就是如此,所以这个是只有类对象,才能调用的魔法方法,如果你不信邪的去使用非类对象调用:
1 print('hello'.__bases__)2
3
4
5
6 Traceback (most recent call last):7 ...8 AttributeError: 'str' object has no attribute '__bases__'
抱歉,只能报错提醒你了,换成别的非类对象,如 "123",也只是把报错信息从‘str’ object has no attribute ‘__bases__’ 换成 ‘int’ object has no attribute ‘__bases__’ 而已
当然,使用类去调用这个魔法方法,完全没有任何问题
1 classPeople(object):2 pass
3
4 print(int.__bases__)5 print(People.__bases__)6 print(type.__bases__)7 print(object.__bases__)8
9
10 (,)11 (,)12 (,)13 ()
没错,你没有看错,object 的基类是个空,而无论是Python 内置的int 类,还是我们自己定义的People 类,又或者是type 这个特殊的类,他们的基类都是object ,这就验证了那句话,object 是所有类的基类!!!