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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM