Python 学习之type 函数的用法

 Warning:本篇为基础学习,由于笔者也在学习中,所以文章过于啰嗦,想直接了解type 函数的用法,推荐“IT技术随笔”的另一篇比较简洁直观的文章:https://www.cnblogs.com/wushuaishuai/p/7739728.html

 

当我们拿到一个变量(对象的引用)时,如何知道这个对象是什么类型,有哪些方法呢?

基本上入门级最开始使用的就是type 函数,针对于刚学习的小小白,我们在根据上文举几个例子,如:

 1 num1 = 123
 2 str1 = 'hello'
 3 noneobj = None
 4 print(type(num1))
 5 print(type(str1))
 6 print(type(noneobj))
 7 
 8 <class 'int'>
 9 <class 'str'>
10 <class 'NoneType'>

当然,我们不使用变量,直接对数据对象本身进行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 <class 'builtin_function_or_method'> # 这一类说明是Python 内置的函数
6 <class 'builtin_function_or_method'>
7 <class 'builtin_function_or_method'>

 

再来看看另外这几种情况,现有另外一个模块demo01.py,里面含有一个自定义函数print01 ,如下使用type 函数:

 1 import time
 2 from copy import deepcopy
 3 import demo01
 4 def fn():
 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(lambda x: x))
12 
13 
14 <class 'function'>
15 <class 'function'>
16 <class 'module'>
17 <class 'module'>
18 <class 'function'>
19 <class 'function'>

由上面测试结果,我们可以分析得知,无论是我们自定义的模块demo01,还是Python 内置的模块time,使用type 函数,得到的结果,都"属于" module 这一类,
而函数方面,无论是我们在demo01.py 里面自定义的print01,或者自定义的匿名函数,还是Python 内置的函数deepcopy ,使用type 函数,得到的结果,都是"属于" function 这一类。

 

另外一种是我们自己定义的类(在demo02.py 中自定义一个Student 类),如下:

 1 class Teacher(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5 
 6 if __name__ == '__main__':
 7     t1 = Teacher('Tom', 22)
 8     print(type(t1))
 9     from demo02 import Student
10     s1 = Student('Jam', 11)
11     print(type(s1))
12 
13 
14 <class '__main__.Teacher'>
15 <class 'demo02.Student'>

这个就没什么说的了,只是介绍一种情况,分别是两个实例对象所属的类,一个是当前模块"__main__"的Teacher 类,另一个是引用模块"demo02" 的Student 类。

 

最后还有一种特殊的情况,比如int,list,set 等这些Python 的强转类型函数,或者是自定义的类,又或者是object 这个Python3 所有类的基类,又或者是type 这个函数本身呢,比如int,list,set 这些内置的数据类型类,或者是自定义的一个类,又或者是object 这个Python3 所有类的基类,又或者是type 这个类(注意,这个type 类和type 函数不是同一个"东西"),他们的对象类型是什么呢?

 1 class A(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 <class 'type'>
12 <class 'type'>
13 <class 'type'>
14 <class 'type'>
15 <class 'type'>
16 <class 'type'>

由此可知,这些都"属于" 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 type
5 ...

 

乌龙事件:说好的万物皆是对象呢?整数"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 <class 'int'>
11 <class 'float'>
12 <class 'tuple'>
13 <class 'NoneType'>

一度兴奋到要给龟叔发邮件什么的,但是经过大佬一提醒:加个括号!想起来元祖中只有一个元素的时候,要加“,” 的原因

1 print((123).__class__)
2 
3 
4 
5 <class 'int'>

好吧,龟叔牛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 import types
 2 import demo02
 3 from copy import deepcopy
 4 
 5 def func():
 6     pass
 7 
 8 print(type(func) == types.FunctionType)
 9 print(type(demo02.fn) == types.FunctionType)
10 print(type(deepcopy) == types.FunctionType)
11 
12 True
13 True
14 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 = lambda x: x
2 
3 print(type(f_la) == types.FunctionType)
4 print(type(f_la) == types.LambdaType)
5 
6 True
7 True

由上面的结果可以得出,匿名函数既是FunctionType 类型的,又是 LambdaType 类型的,这个本来就没问题,匿名函数本来就是函数的一个分支嘛~

 

说完了一个参数的,我们再来说说另外一个至少是我比较少用的用法,传入三个参数

光标在type 函数后面的括号里面按住"ctrl + P"

需要传入一个"str" 类型的name,类 的名称,

需要传入一个"tuple" 类型的bases,前面传入name 实参的基类的元祖,考虑多继承

需要传入一个"dict" 类型的dict,为一个字典,包含类定义内的命名空间变量,看下面的例子

 1 class People(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 xiaoming
11 30
12 China

 

 

扩展

1.说完这些后,我们再看看IDE 给我们报的警告,以最后一个LambdaType 的例子为例

大致意思就是不建议使用"==" 对类型进行比较,推荐使用isinstance 函数,那么使用isinstance 函数,有什么好处呢?或者说对比type 函数,进行判断的时候,有什么不同呢?

我先直接说出答案,然后再具体举例子说明:

type 函数不认为子类是父类类型的一种,即不考虑继承关系

isinstance 函数,认为字类是父类类型的一种,即考虑继承关系

所以如果判断两个对象类型是否相等,PEP8 规范推荐使用isinstance 函数,具体代码如下:

class People(object):
    pass

class Teacher(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 class type(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 class People(object):
 2     pass
 3 
 4 print(int.__bases__)
 5 print(People.__bases__)
 6 print(type.__bases__)
 7 print(object.__bases__)
 8 
 9 
10 (<class 'object'>,)
11 (<class 'object'>,)
12 (<class 'object'>,)
13 ()

没错,你没有看错,object 的基类是个空,而无论是Python 内置的int 类,还是我们自己定义的People 类,又或者是type 这个特殊的类,他们的基类都是object ,这就验证了那句话,object 是所有类的基类!!!

 

posted @ 2019-02-27 12:55  Gnbp  阅读(31600)  评论(1编辑  收藏  举报