• 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 是所有类的基类!!!

  • 相关阅读:
    php 7.1 openssl_decrypt() 代替 mcrypt_module_open() 方法
    关于Http_build_query的用法
    git fetch 和git pull 的差别
    PhpStorm 头部注释、类注释和函数注释的设置
    输出信息log4j.properties的作用与使用方法
    字段设置ALV中下拉列表列的实现
    遍历中序C语言实现二叉树的递归遍历与非递归遍历
    搜索中文Solr Analysis And Solr Query Solr分析以及查询
    记忆指向指针常量 常量指针 常量指针常量
    匹配位置KMP算法深入浅出
  • 原文地址:https://www.cnblogs.com/yungiu/p/10442999.html
Copyright © 2020-2023  润新知