python中的type和object詳解

關於這篇博客

這篇博客主要描述Python的新風格對象(new-style objects),以下:html

  1. <type 'type'>和<type 'object'>分別是什麼?
  2. 用戶自定義的類和實例之間是怎麼聯繫的?它們和內置類型又是怎麼聯繫的?
  3. 什麼是元類(metaclasses)?

New-style代表這篇博客所說的內容只適用於版本爲2.2及以上的python。python


開始以前

最主要,是理解type和object的區別與聯繫。咱們平時用的最多的是Object,好比你定義一個類時,會繼承object:python3.x

>>> class Test(object):
...     pass

這裏你定義了一個自定義類Test,不難看出,Test繼承了object,也就是說,objectTest的超類(或者說基類)。
接下來,你能夠再定義一個類:ssh

>>> class subTest(Test):
...     pass

subTest繼承了Test,同時,由於Test繼承了object,因此也能夠說subTest繼承了object。在這裏涉及到一個重要的知識點,那就是繼承具備傳遞性。若是你仔細觀察,你會發現另一個知識點,那就是:object是全部類的超類(這句話相當重要)。那type是什麼呢?它是object的類型(也就是說object是type的實例),同時,object又是type的超類。函數

type是object的類型,同時,object又是type的超類」這句話看起來就充滿疑點:那究竟是先有object仍是先有type呢?其實,「先有object和仍是type問題」就像「先有雞仍是先有蛋問題」。到底先有誰呢?不急,請繼續看:學習

你要明白這些,先要知道,python是面向對象的語言。在python裏面,一切皆爲對象測試

一切皆爲對象?這裏對於一部分人來講,可能不是很容易理解。這麼說吧,在python裏,int整形是對象,整數2也是對象,你定義的函數啊,類啊都是對象,你定義的變量也是對象。總之,你在python裏能用到的均可以稱之爲對象。ui

好了,把python裏一切皆爲對象給整明白後,你要明白在面向對象的體系中,存在兩種關係:spa

  • 父子關係(圖中以實線描述):這種關係存在於某個類(subclass)是另外一個類(superclass)的特別版本之中。一般描述爲「子類是一種父類」。好比:蛇是一種爬行動物(Snake is a kind of reptile)。其中,蛇(snake)是子類,爬行動物(reptile)是父類。蛇擁有爬行動物的特徵,同時,又擁有標誌本身是一條蛇的特徵。
  • 類型實例關係(圖中以虛線描述):這種關係存在於兩個對象之中,其中一個對象(實例)是另外一個對象(類型)的具體實現。我有一條寵物蛇叫Squasher,那麼Squasher就是蛇的一個實例。英文描述爲:"Squasher is an instance of snake".

用實線表示父子關係,是由於父與子的關係更加「貼實」。好比有人叫你列出有關蛇的詞,你可能會說蛇是爬行動物,但你不會說出蛇是Squasher....
我想若是把上面的兩種關係用代碼表示出來你會更加直觀:翻譯

>>> class reptile(object):
...     feature = "有標誌本身是爬行動物的特徵"
...     name = "爬行動物"
...
>>> class snake(reptile):
...     snake_feature = "除了有標誌本身是爬行動物特徵,還有本身是蛇的特徵"
...     name = "蛇"
...
>>> Squasher = snake()

class reptile(object)class snake(reptile)就是表明父子關係。object是reptile的基類,reptile是snake的超類(基類)。這裏有沒有想起來 object是全部類的超類
Squasher = snake()是類型實例關係。將類snake實例化就獲得了Squasher。

這時候,有兩條頗有用的規則:

  1. Dashed Arrow Up Rule:If X is an instance of A, and A is a subclass of B, then X is an instance of B as well.翻譯過來應該是「虛線向上規則」:若是X是A的實例,同時A又是B的子類,那麼,X也是B的實例。
  2. Dashed Arrow Down Rule:If B is an instance of M, and A is a subclass of B, then A is an instance of M as well.翻譯過來應該是「虛線向下規則」:若是B是M的實例,同時A是B的子類,那麼,A也是M的實例。其實這條規則不多會用到,但卻和這篇博客要講的內容息息相關。我來略做分析,從「若是B是M的實例」這句話得出,B是實例,「A是B的子類」 --> B是一個(父)。B是實例,同時又是一個類?怎麼回事?看完這篇博客,你會知道答案的。

在這裏,我來解釋一下爲何叫"虛線向上規則",經過觀察上圖右邊,咱們能夠清晰地見到一個帶箭頭的虛線,從X端出發,射向A端,此時,A端爲箭頭端,虛線表明類型實例關係,因此A端是類型,即X是A的實例(換句話說,A是X的類型),經過命令X.__class__咱們可查看X的類型。再看,一條帶箭頭的實線從A端射向B端,B端是箭頭端,實線表明父子關係,因此B端是父類,即A是B的子類這時候,咱們經過將X端射向A端的虛線,向上擡,射向B端(你應該能夠看到上圖右上方有一條標誌爲implied[這個單詞意思是隱藏]的向上虛線),就實現了表述X也是是B的實例的目的。也名副其實,虛線向上嘛。虛線向下規則也能夠這樣推出來,我就不演示了。

總的來講,面向對象體系裏,有兩種關係,一種是父子關係,經過父類與子類來描述,另外一種是類型實例關係,經過類和實例來描述。而兩條規則,是將類之間,類與實例之間的關係聯繫在一塊兒。

到這裏,能夠進入主題了。


基本概念

對象內部:The Object Within

上面咱們說了面向對象,那麼對象(object)是什麼呢?對象是python的重要核心之一:它是某個實體的抽象描述。對象擁有下面的特徵:

  1. 對象標識(Identity):同一個(父)類實例化出來的對象,每每具備許多相同的特徵,可是這些具備許多相同特徵的對象確定都有一個惟一的對象標識,固然,不一樣父類實例化出來的對象也是用對象標識做爲判斷對象的條件之一的;
  2. 值(A value):這意味着對象包含一堆屬性。咱們能夠經過objectname.attributename的方式操做屬性;
  3. 類型(A type):每一個對象都有一個確切地類型。例如,對象「2」的類型是int;
  4. 一個或多個「Bases」(One or more bases):不是全部對象都有Bases,但一些特殊的對象會有,好比:。Bases相似於面嚮對象語言中的「基類」,「超類」。

若是你想知道一個對象在內存中的位置,你能夠調用id(對象)來查看。在這裏,我再次強調,在python中,一切都有對象的概念。數字2是對象,類型int也是對象...
typeBases(若是它們存在)很是重要,由於它們定義了一個對象和另外一個對象之間的關係。請記住,typeBases自己也是對象,稍後會提到。
你也許會認爲,對象有名字,但名字並非對象的組成部分。對象的名字存在於這個對象的命名空間(namespace)以外或者是另外一個對象的屬性。也就是說:名字和這個對象不是存儲在同一個「地方」

例子:測試一個整數對象

>>> two = 2  #==>(1)
>>> type(two)  #==>(2)
<class 'int'>
>>> type(type(two))  #==>(3)
<class 'type'>
>>> type(two).__bases__  #==>(4)
(<class 'object'>,)
>>> dir(two)  #==>(5)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

(1):咱們在當前命名空間給數字2分配一個名字。並將2和「two」綁定在起。
(2):這個對象的類型是<class 'int'>。你會在其它地方見到相似<type 'int'>int這樣的輸出,其實它們都是一個意思。
(3):額,<class 'int'>的類型是<class 'type'>.
(4):輸出「類int」的基類。
(5):列出整型這個對象全部的屬性。

可能會以爲有點亂,我稍微總結一下:數字2是類型int(通常來說,在python中「類」和「類型」是同一種東西)的一個實例。倒過來講,int是數字2的類型。元組(<class 'object'>,)是類型int的超類(或說父類)。一個類型的超類可能不止一個,因此用元組表示。
如今,咱們引出第一條規則:

一切皆爲對象
上面說的數字2,類型int,int的超類<class 'object'>都是對象。除此以外,你定義的函數,方法...都是對象。


一塊乾淨的畫板

如今咱們來創建python的對象系統。從一塊乾淨的畫板開始...,畫板分爲三部分,從左到右,分別表明類的類,類,類的實例。

咱們會在這個畫板中開啓咱們的學習之旅...


關係(Relationships)

咱們談及對象時,用兩種關係來鏈接各類對象,使得對象之間產生聯繫:

  1. 父子關係( the subclass-superclass relationship);
  2. 類型實例關係( the type-instance relationship )。

在文章開頭已經詳細討論過這兩種關係了。


進入對象( Bring In The Objects)

第一個對象

咱們測試兩個對象:objecttype:
例子1:

>>> 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的類型是它本身

咱們把例子1獲取的信息描述在畫板上:

object和type是python中的兩個源對象,當咱們嘗試介紹它們是就會陷入「先有雞仍是現有蛋」難題,到底先介紹誰?事實上,它們是互相依賴對方來定義,因此它們不能分開而論。

繼續咱們的python實驗:

>>> isinstance(object,object)    #===>(1)
True
>>> isinstance(type, object)     #===>(2)
True

(1):發生了什麼?其實這裏利用了虛線向上規則,type是object的子類,type的實例天然也是object的實例。object是type的實例啊。
(2):這裏我參考的英文文檔解釋是:同時應用虛線向上和虛線向下規則。但我看得一臉懵逼。由於我覺的這裏和(1)同樣啊:type是object的子類,type的實例天然也是object的實例。type也是type的實例啊。

若是你認爲上面的解釋很混亂,不用理會它。不影響你理解這篇文章的主要目的。

新概念: type objects

typeobject都屬於type objects。type objects翻譯過來就是類型對象了。類型對象的特徵:

  1. 它們用於表示程序中的抽象數據類型。例如,咱們定義的一個類User會表明系統中全部的用戶。int會表明系統中全部整形數字。
  2. 它們能被繼承。這意味着你能夠利用存在的類型對象創造出新的類型對象。已經存在的類型對象是新的類型對象的超類。
  3. 它們能被實例化。這意味着你能夠利用已經存在的類型對象創造出新的實例對象。前者是後者的type
  4. 類型對象的類型是type
  5. 它們有時會被成爲類型有時會被稱爲

你沒有看錯。在新版本的python中類型已是同同樣東西了。由一個很明顯的地方就能夠看出來。__class__type()的輸出是同樣的。

在舊版本的python中,是特指用class語句創造出來的東西。而內置類型例如int通常不會被認爲是,而是被認爲是類型。但在新版本中它們是同同樣東西了。我以爲有必要爲這個改變定義一條規則:
類是類型,類型也是類(Class is Type is Class)

在>=2.3版本的python中,類和類型是同同樣東西。
The term type is equivalent to the term class in all version of Python >= 2.3.

類型和非類型(或者說類和非類)都是對象,但只有類型可以被繼承。非類型擁有具體的值,因此被繼承是毫無心義的,並且它也不能被繼承。作簡單的例子,就是類型int和它的實例2。int是類型,2是非類型。你說說,繼承2有什麼意義?

是否仍是會疑惑到底社麼是類型?什麼是非類型?
這裏有一條判斷規則送給你:

若是一個對象,它的類型是「<class 'type'>」,那麼,它是類型,不然不是。

還記得怎麼判斷一個對象的類型嗎?沒錯的,__class__和type()隨意你用。

小總結:

  • <class 'object'>的類型是<class 'type'>;
  • <class 'object'>的父類爲空;
  • <class 'type'> 的類型是它本身自己;
  • <class 'type'> 是<class 'object'>的子類;
  • 在python中只要兩種對象:類型和非類型。非類型也被稱爲實例。這裏有英文原句,我不知怎麼翻譯了,很容易看懂,但不知如何說:There are only two kinds of objects in Python: to be unambiguous let's call these types and non-types. Non-types could be called instances, but that term could also refer to a type, since a type is always an instance of another type. Types could also be called classes, and I do call them classes from time to time.

注意咱們在畫板中只畫出兩個對象的直接關係,隱藏的關係就不畫了,節省咱們的精力和畫板尺寸


更多內置對象

python這條船上可不止只有兩個源對象。經過這兩個源對象能夠繁育出一堆對象:
Figure 2.2. 一些內置對象

上圖中的一些內置類型,下面經過例子來測試:

>>> list    #====>(1)
<class 'list'>
>>> list.__class__    #====>(2)
<class 'type'>
>>> list.__bases__    #====>(3)
(<class 'object'>,)
>>> tuple.__class__,tuple.__bases__    #====>(4)
(<class 'type'>, (<class 'object'>,))
>>> dict.__class__,dict.__bases__    #和(4)同樣原理
(<class 'type'>, (<class 'object'>,))
>>> mylist = [1,2,3]    #====>(5)
>>> mylist.__class__    #====>(6)
<class 'list'>
>>> mylist.__bases__    #====>(7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__bases__'

(1):內置對象list;
(2):list的類型是<class 'type'>;
(3):list的超類是(<class 'object'>,);
(4):內置對象tuple.tuple的類型和超類分別是:<class 'type'>, (<class 'object'>,);
(5)list的一個實例mylist;
(6)實例mylist的類型是<class 'list'>;
(7)實例沒有超類。

咱們能夠創造一個tuple或dict的實例,但咱們不能創造一個mylist的實例。由於mylist不是一個類型,它僅僅是一個實例。

經過繼承產生新對象

內置類型是python自己就有的。那麼咱們如何創造一個新的類型呢?
新的類型不可以憑空產生,它必須依賴已經存在的類型,因而,繼承就呼之欲出了。

例子:經過繼承產生新對象

# 在 Python 2.x,你得顯式寫出自定義的類繼承於object:
class C(object):    #====>(1)
    pass

# In Python 3.x,不用顯式寫出object,若是你不寫,則自動繼承於object:
class C:    #====>(2)
    pass

class D(object):
    pass

class E(C, D):    #====>(3)
    pass

class MyList(list):    #====>(4)
    pass

(1):class語句告訴python解釋器要經過一個存在的類型創造一個新的類型;
(2):在python3.x能夠省略掉(object)。
(3):多重繼承;
(4):大多數內置類型能夠被繼承,但不是全部的均可以。

經過實例化產生新對象

Example 2.5.

obj = object()    #====>(1)

cobj = C()   #====>(2)

mylist = [1,2,3]    #====>(3)

(1),(2):利用類型名()的方式創造一個類型的實例。()中可能帶參數;
(3):這是python利用內置類型創造實例的語法。沒什麼好說的。

注意:僅僅經過對<class 'object'>進行子類化,類型C就自動成爲<class 'type'>的實例。緣由在常見疑問的第二個問題中。

在以上的操做後,本來空白的畫板能夠畫滿了:


常見疑問

到這裏你頭腦中可能會有不少疑惑,下面列出其中一些問題以及答案,請酌情欣賞。有沒有提到的請留言,我會努力搜索答案來解答:
Q: Python如何真正建立一個新對象?
A: 在python中,創造的新對象有兩種:類型和非類型。類型可被繼承和實例化。非類型本事就是一個實例。當python創造新對象時,它會用本身自己的類型做爲新對象的類型。通常會用到兩個方法__new__()__init__()。因此。每一個對象都有類型。
Q:實例化的時候要指定類型,可是當繼承時python如何知道用哪一個類型?
它查看你繼承的超類,而且使用超類的類型來做爲新對象的類型。
在大多數的狀況下,<class 'object'>的子類(和<class 'object'>的子類的子類等等)的類型都是<class 'type'>

>>> class A(object):
...     pass
...
>>> class B(A):
...     pass
...
>>> class C(B):
...     pass
...
>>> A.__class__, B.__class__, C.__class__
(<class 'type'>, <class 'type'>, <class 'type'>)

Q:我可以創造一個新的類型?
A:能,這就得元類出場了,經過屬性__metaclass__你能夠從新創造一個類型出來。這裏我簡單列一個例子。元類的話下面會簡單介紹。

>>> class A(type):
...     pass
...
>>> class B(object, metaclass=A):
...     pass
...
>>> class C(B):
...     pass
...
>>> A.__class__, B.__class__, C.__class__
(<class 'type'>, <class '__main__.A'>, <class '__main__.A'>)

經過繼承type,咱們創造出新的類型<class '__main__.A'>

Q:wow!那我可使用任何的類型做爲metaclass的參數嗎?
A:不能。只有繼承了type的類可以作爲metaclass的參數。

Q:我應該使用metaclass嗎?
不建議使用。高手除外。


準備結束

一幅描繪python對象的圖

咱們最後獲得一幅由不一樣對象組成的地圖:

在大多數狀況之下,咱們都是學習第二列和第三列的內容。至於第一列,那是元類的領域了。不是全部人都要深刻學習。

來解釋一下上圖的東西:

  1. 虛線能夠從一列穿過另外一列,例如從實例所在列穿到類所在列。(<type 'type'>例外);
  2. 實線不能夠穿過其餘列。再一次地,<type 'type'> -> <type 'object'> 是例外。
  3. 第三列不容許出現實線。由於實線表明繼承。第三列地實例沒法子類化;
  4. 第三列地對象也不容許被實例化;
  5. 第一,二列包含類型,第三列包含非類型;
  6. 若是創造一個繼承<class 'type'>的對象,那麼它會被放在第一列,即元類。這裏繼續強調,類和類型是同樣的。<class 'type'><type 'type'>也是同樣的。

注意:<class 'type'>是全部類型的類型。<class 'object'>也是全部對象的超類(除了它本身)


總結

這些內容是對前面的總結:

  1. 在python中有兩種對象:
    • 類型對象:能夠被實例化和繼承;
    • 非類型對象:不能夠被實例和繼承。
  2. <class 'type'>和<class 'object'>是python中的兩個源對象。
  3. 每一個對象都有類型。用objectname.__class__查看。
  4. 每一個類型對象都有超類(object除外),用objectname.__bases__能夠查看。
  5. 經過繼承產生的新對象都是類型對象。繼承是用class語句來實現的。
  6. 經過實例化產生的新對象多是類型對象,也多是非類型對象。你看下圖,虛線就表示實例化,第一列和第二列實例化產生的新對象就是類型對象。第三列實例化產生的新對象就是非類型對象。實例化是經過調用操做符()來實現的。好比你自定義了一個類myclass,實例化就是在myclass後增長()操做符完成的。也就是instance_of_myclass=myclass()

  7. 一些python的非類型對象能夠經過特殊的語法來創造。例如[1, 2, 3]是list的實例。
  8. 在內部,python老是使用類型對象來創造新對象。新創造的對象是該類型對象的實例。(在這裏,實例有兩種意思:一經過繼承產生的子類,二是經過實例化產生的具體實例。但平時咱們說的實例就是隻第二種)。python經過class語句中指定的超類的類型來決定新對象的類型
  9. issubclass(A,B)返回true當且僅當:
    • B在A.__bases__輸出的元組之中;
    • 若是A在Z.__bases__輸出的元組中,issubclass(Z,B)返回true.
  10. isinstance(A,B)返回true當且僅當:
    • A.__class__是B,或者
    • issubclass(A.__class__,B)返回true.

完結

這篇文章很長。由於水平限制,可能會出現錯誤。請批判性閱讀。仍是建議閱讀英文原文 (英語原文失效了,若是你能上世界互聯網,請自行搜索,關鍵詞:python types and objects)


版權:保留全部權,轉載註明出處。

相關文章
相關標籤/搜索