AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

解密 Python 的變量和對象,它們之間有什么區別和聯系呢?

來源: 責編: 時間:2024-05-08 09:17:06 136觀看
導讀Python 中一切皆對象在學習 Python 的時候,你肯定聽過這么一句話:Python 中一切皆對象。沒錯,在 Python 世界里,一切都是對象。整數是一個對象、字符串是一個對象、字典是一個對象,甚至 int, str, list 以及我們使用 class

Python 中一切皆對象

unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

在學習 Python 的時候,你肯定聽過這么一句話:Python 中一切皆對象。沒錯,在 Python 世界里,一切都是對象。整數是一個對象、字符串是一個對象、字典是一個對象,甚至 int, str, list 以及我們使用 class 關鍵字自定義的類,它們也是對象。unL28資訊網——每日最新資訊28at.com

像 int, str, list 等基本類型,以及自定義的類,由于它們可以表示類型,因此我們稱之為類型對象;類型對象實例化得到的對象,我們稱之為實例對象。但不管是哪種對象,它們都屬于對象。unL28資訊網——每日最新資訊28at.com

因此 Python 將面向對象理念貫徹的非常徹底,面向對象中的類和對象在 Python 中都是通過對象實現的。unL28資訊網——每日最新資訊28at.com

在面向對象理論中,存在著類和對象兩個概念,像 int、dict、tuple、以及使用 class 關鍵字自定義的類型對象實現了面向對象理論中類的概念,而 123、3.14,"string" 等等這些實例對象則實現了面向對象理論中對象的概念。但在 Python 里面,面向對象的類和對象都是通過對象實現的。unL28資訊網——每日最新資訊28at.com

我們舉個例子:unL28資訊網——每日最新資訊28at.com

# dict 是一個類,因此它屬于類型對象# 類型對象實例化得到的對象屬于實例對象print(dict)"""<class 'dict'>"""print(dict(a=1, b=2))"""{'a': 1, 'b': 2}"""

因此可以用一張圖來描述面向對象在 Python 中的體現。unL28資訊網——每日最新資訊28at.com

圖片圖片unL28資訊網——每日最新資訊28at.com

而如果想查看一個對象的類型,可以使用 type,或者通過對象的 __class__ 屬性。unL28資訊網——每日最新資訊28at.com

numbers = [1, 2, 3]# 查看類型print(type(numbers))"""<class 'list'>"""print(numbers.__class__)"""<class 'list'>"""

如果想判斷一個對象是不是指定類型的實例對象,可以使用 isinstance。unL28資訊網——每日最新資訊28at.com

numbers = [1, 2, 3]# 判斷是不是指定類型的實例對象print(isinstance(numbers, list))"""True"""

但是問題來了,按照面向對象的理論來說,對象是由類實例化得到的,這在 Python 中也是適用的。既然是對象,那么就必定有一個類來實例化它,換句話說對象一定要有類型。unL28資訊網——每日最新資訊28at.com

至于一個對象的類型是什么,就看這個對象是被誰實例化的,被誰實例化那么類型就是誰,比如列表的類型是 list,字典的類型是 dict 等等。unL28資訊網——每日最新資訊28at.com

而 Python 中一切皆對象,所以像 int, str, tuple 這些內置的類對象也是具有相應的類型的,那么它們的類型又是誰呢?使用 type 查看一下就知道了。unL28資訊網——每日最新資訊28at.com

>>> type(int)<class 'type'>>>> type(str)<class 'type'>>>> type(dict)<class 'type'>>>> type(type)<class 'type'>

我們看到類型對象的類型,無一例外都是 type。而 type 我們也稱其為元類,表示類型對象的類型。至于 type 本身,它的類型還是 type,所以它連自己都沒放過,把自己都變成自己的對象了。unL28資訊網——每日最新資訊28at.com

因此在 Python 中,你能看到的任何對象都是有類型的,可以使用 type 查看,也可以獲取該對象的 __class__ 屬性查看。所以:實例對象、類型對象、元類,Python 中任何一個對象都逃不過這三種身份。unL28資訊網——每日最新資訊28at.com

到這里可能有人會發現一個有意思的點,我們說 int 是一個類對象,這顯然是沒有問題的。因為站在整數(比如 123)的角度上,int 是一個不折不扣的類對象;但如果站在 type 的角度上呢?顯然我們又可以將 int 理解為實例對象,因此 class 具有二象性。unL28資訊網——每日最新資訊28at.com

至于 type 也是同理,雖然它是元類,但本質上也是一個類對象。unL28資訊網——每日最新資訊28at.com

注:不僅 type 是元類,那些繼承了 type 的類也可以叫做元類。unL28資訊網——每日最新資訊28at.com

然后 Python 中還有一個關鍵的類型(對象),叫做 object,它是所有類型對象的基類。不管是什么類,內置的類也好,我們自定義的類也罷,它們都繼承自 object。因此 object 是所有類型對象的基類、或者說父類。unL28資訊網——每日最新資訊28at.com

那如果我們想獲取一個類都繼承了哪些基類,該怎么做呢?方式有三種:unL28資訊網——每日最新資訊28at.com

class A: passclass B: passclass C(A): passclass D(B, C): pass# 首先 D 繼承自 B 和 C, C 又繼承 A# 我們現在要來查看 D 繼承的父類# 方法一: 使用 __base__print(D.__base__)  """<class '__main__.B'>"""# 方法二: 使用 __bases__print(D.__bases__)  """(<class '__main__.B'>, <class '__main__.C'>)"""# 方法三: 使用 __mro__print(D.__mro__)"""(<class '__main__.D'>, <class '__main__.B'>,  <class '__main__.C'>, <class '__main__.A'>,  <class 'object'>)"""
  • __base__:如果繼承了多個類,那么只顯示繼承的第一個類,沒有顯式繼承則返回 <class 'object'>
  • __bases__:返回一個元組,會顯示所有直接繼承的父類,沒有顯式繼承則返回 (<class 'object'>,)
  • __mro__: mro(Method Resolution Order)表示方法查找順序,會從自身出發,找到最頂層的父類。因此返回自身、繼承的基類、以及基類繼承的基類, 一直找到 object

而如果想查看某個類型是不是另一個類型的子類,可以通過 issubclass。unL28資訊網——每日最新資訊28at.com

print(issubclass(str, object))"""True"""

因此,我們可以得出以下兩個結論:unL28資訊網——每日最新資訊28at.com

  • type 站在類型金字塔的最頂端,任何一個對象按照類型追根溯源,最終得到的都是 type;
  • object 站在繼承金字塔的最頂端,任何一個類型對象按照繼承關系追根溯源,最終得到的都是 object;

但要注意的是,我們說 type 的類型還是 type,但 object 的基類則不再是 object,而是 None。unL28資訊網——每日最新資訊28at.com

print(    type.__class__)  # <class 'type'># 注:以下打印結果容易讓人產生誤解# 它表達的含義是 object 的基類為空# 而不是說 object 繼承 Noneprint(    object.__base__)  # None

但為什么 object 的基類是 None,而不是它自身呢?其實答案很簡單,Python 在查找屬性或方法的時候,自身如果沒有的話,會按照 __mro__ 指定的順序去基類中查找。所以繼承鏈一定會有一個終點,否則就會像沒有出口的遞歸一樣出現死循環了。unL28資訊網——每日最新資訊28at.com

我們用一張圖將對象之間的關系總結一下:unL28資訊網——每日最新資訊28at.com

圖片圖片unL28資訊網——每日最新資訊28at.com

  • 實例對象的類型是類型對象,類型對象的類型是元類;
  • 所有類型對象的基類都收斂于 object;
  • 所有對象的類型都收斂于 type;

因此 Python 算是將一切皆對象的理念貫徹到了極致,也正因為如此,Python 才具有如此優秀的動態特性。unL28資訊網——每日最新資訊28at.com

但是還沒結束,我們再重新審視一下上面那張圖,會發現里面有兩個箭頭看起來非常的奇怪。object 的類型是 type,type 又繼承了 object。unL28資訊網——每日最新資訊28at.com

>>> type.__base__<class 'object'>>>> object.__class__<class 'type'>

因為 type 是所有類的元類,而 object 是所有類的基類,這就說明 type 要繼承自 object,而 object 的類型是 type。很多人都會對這一點感到奇怪,這難道不是一個先有雞還是先有蛋的問題嗎?其實不是的,這兩個對象是共存的,它們之間的定義其實是互相依賴的。而具體是怎么一回事,我們后續分析。unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

Python 的變量其實是指針

unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

Python 的變量只是一個名字,如果站在 C 語言的角度來看,那么就是一個指針。所以 Python 的變量保存的其實是對象的內存地址,或者說指針,而指針指向的內存存儲的才是對象。unL28資訊網——每日最新資訊28at.com

所以在 Python 中,我們都說變量指向了某個對象。在其它靜態語言中,變量相當于是為某塊內存起的別名,獲取變量等于獲取這塊內存所存儲的值。而 Python 中變量代表的內存所存儲的不是對象,而是對象的指針(或者說引用)。unL28資訊網——每日最新資訊28at.com

我們舉例說明,看一段 C 代碼。unL28資訊網——每日最新資訊28at.com

#include <stdio.h>void main(){    int a = 666;    printf("address of a = %p/n", &a);    a = 667;    printf("address of a = %p/n", &a);}

編譯執行一下:unL28資訊網——每日最新資訊28at.com

圖片圖片unL28資訊網——每日最新資訊28at.com

賦值前后地址都是 0x7fff9eda521c,沒有變化,再來看一段 Python 代碼。unL28資訊網——每日最新資訊28at.com

a = 666print(hex(id(a)))  # 0x7febf803a3d0a = 667print(hex(id(a)))  # 0x7fec180677b0

我們看到 Python 里面輸出的地址發生了變化,下面分析一下原因。unL28資訊網——每日最新資訊28at.com

首先在 C 中,創建一個變量的時候必須規定好類型,比如 int a = 666,那么變量 a 就是 int 類型,以后在所處的作用域中就不可以變了。如果這時候再設置 a = 777,那么等于是把內存中存儲的 666 換成 777,a 的地址和類型是不會變化的。unL28資訊網——每日最新資訊28at.com

而在 Python 中,a = 666 等于是先開辟一塊內存,存儲的值為 666,然后讓變量 a 指向這片內存,或者說讓變量 a 保存這塊內存的地址。然后 a = 777 的時候,再開辟一塊內存,然后讓 a 指向存儲 777 的內存,由于是兩塊不同的內存,所以它們的地址是不一樣的。unL28資訊網——每日最新資訊28at.com

圖片圖片unL28資訊網——每日最新資訊28at.com

所以 Python 的變量只是一個和對象關聯的名字,它代表的是對象的指針。換句話說 Python 的變量就是個便利貼,可以貼在任何對象上,一旦貼上去了,就代表這個對象被引用了。unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

值傳遞?引用傳遞?

unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

再來看看變量之間的傳遞,在 Python 中是如何體現的。unL28資訊網——每日最新資訊28at.com

a = 666print(hex(id(a)))  # 0x1f4e8ca7fb0b = aprint(hex(id(b)))  # 0x1f4e8ca7fb0

我們看到打印的地址是一樣的,再用一張圖解釋一下。unL28資訊網——每日最新資訊28at.com

圖片圖片unL28資訊網——每日最新資訊28at.com

a = 666 的時候,先開辟一份內存,再讓 a 存儲對應內存的地址;然后 b = a 的時候,會把 a 拷貝一份給 b,所以 b 和 a 存儲了相同的地址,它們都指向了同一個對象。unL28資訊網——每日最新資訊28at.com

因此說 Python 是值傳遞、或者引用傳遞都是不準確的,準確的說 Python 是變量的值傳遞,對象的引用傳遞。因為 Python 的變量可以認為是 C 的一個指針,在 b = a 的時候,等于把 a 指向的對象的地址(a 本身)拷貝一份給 b,所以對于變量來說是值傳遞;然后 a 和 b 又都是指向對象的指針,因此對于對象來說是引用傳遞。unL28資訊網——每日最新資訊28at.com

在這個過程中,對象沒有重復創建,它只是多了一個引用。unL28資訊網——每日最新資訊28at.com

另外還有最關鍵的一點,Python 的變量是一個指針,當傳遞變量的時候,傳遞的是指針;但是在操作變量的時候,會操作變量指向的內存。所以 id(a) 獲取的不是 a 的地址,而是 a 指向的內存的地址(在底層其實就是 a 本身);同理 b = a,是將 a 本身,或者說將 a 存儲的、指向某個具體的對象的地址傳遞給了 b。unL28資訊網——每日最新資訊28at.com

另外在 C 的層面,顯然 a 和 b 屬于指針變量,那么 a 和 b 有沒有地址呢?顯然是有的,只不過在 Python 中是獲取不到的,解釋器只允許獲取對象的地址。unL28資訊網——每日最新資訊28at.com

我們再舉個函數的例子:unL28資訊網——每日最新資訊28at.com

def some_func(num):    print("address of local num", hex(id(num)))    num = 667    print("address of local num", hex(id(num)))num = 666print("address of global num", hex(id(num)))some_func(num)"""address of global num 0x2356cd698d0address of local num 0x2356cd698d0address of local num 0x2356c457f90"""

函數的參數也是一個變量,所以 some_func(num) 其實就是把全局變量 num 存儲的對象的地址拷貝一份給局部變量 num,所以兩個 num 指向了同一個對象,打印的地址相同。unL28資訊網——每日最新資訊28at.com

然后在函數內部執行 num = 667,相當于讓局部變量指向新的對象,或者說保存新對象的地址,因此打印的結果發生變化。unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

變量有類型嗎?

unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

當提到類型時,這個類型指的是變量的類型還是對象的類型呢?不用想,肯定是對象的類型。因為 Python 的變量是個指針,操作指針會自動操作它指向的內存,所以使用 type(a) 查看的其實是變量 a 指向的對象的類型。unL28資訊網——每日最新資訊28at.com

那么問題來了,我們在創建變量的時候,并沒有顯式地指定類型啊,那么 Python 是如何判斷一個變量指向的是什么類型的數據呢?答案是:解釋器是通過靠猜的方式,通過賦的值(或者說變量引用的值)來推斷類型。unL28資訊網——每日最新資訊28at.com

因此在 Python 中,如果你想創建一個變量,那么必須在創建變量的時候同時賦值,否則解釋器就不知道這個變量指向的數據是什么類型。所以 Python 是先創建相應的值,這個值在 C 中對應一個結構體,結構體里面有一個成員專門用來存儲該值對應的類型,因此在 Python 中,類型是和對象綁定的,而不是和變量。當創建完值之后,再讓這個變量指向它,所以 Python 中是先有值后有變量。unL28資訊網——每日最新資訊28at.com

但顯然在 C 里面不是這樣的,因為 C 的變量代表的內存所存儲的就是具體的值,所以在 C 里面可以直接聲明一個變量的同時不賦值。因為 C 要求聲明變量時必須指定類型,因此變量聲明之后,其類型和內存大小就已經固定了。unL28資訊網——每日最新資訊28at.com

而 Python 的變量存的是個地址,它只是指向了某個對象,所以由于其便利貼的特性,可以貼在任意對象上面。但是不管貼在哪個對象,都必須先有對象才可以,不然變量貼誰去。unL28資訊網——每日最新資訊28at.com

另外,盡管 Python 在創建變量的時候不需要指定類型,但 Python 是強類型語言,而且是動態強類型。unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

小結

unL28資訊網——每日最新資訊28at.com

unL28資訊網——每日最新資訊28at.com

以上我們就聊了聊 Python 的變量和對象,核心就在于:變量保存的不是對象本身,而是對象的內存地址,站在 C 的角度上看變量就是一個指針。unL28資訊網——每日最新資訊28at.com

盡管 Python 一切皆對象,但你拿到的都是對象的指針,變量是一個指針,函數是一個指針,元組、列表、字典里面存儲的還是指針。我們可以想象一下列表,它底層是基于數組實現的,由于 C 數組要求里面的每個元素的類型和大小都相同,因此從這個角度上講,列表內部存儲的只能是指針。unL28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-87253-0.html解密 Python 的變量和對象,它們之間有什么區別和聯系呢?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: TypeScript封裝一個根據背景色顯示適合的字體顏色

下一篇: JavaScript 原生深拷貝方法來啦!structuredClone 閃耀登場~

標簽:
  • 熱門焦點
  • 一文看懂為蘋果Vision Pro開發應用程序

    譯者 | 布加迪審校 | 重樓蘋果的Vision Pro是一款混合現實(MR)頭戴設備。Vision Pro結合了虛擬現實(VR)和增強現實(AR)的沉浸感。其高分辨率顯示屏、先進的傳感器和強大的處理能力
  • 分享六款相見恨晚的PPT模版網站, 祝你做出精美的PPT!

    1、OfficePLUSOfficePLUS網站旨在為全球Office用戶提供豐富的高品質原創PPT模板、實用文檔、數據圖表及個性化定制服務。優點:OfficePLUS是微軟官方網站,囊括PPT模板、Word模
  • 量化指標是與非:挽救被量化指標扼殺的技術團隊

    作者 | 劉新翠整理 | 徐杰承本文整理自快狗打車技術總監劉新翠在WOT2023大會上的主題分享,更多精彩內容及現場PPT,請關注51CTO技術棧公眾號,發消息【WOT2023PPT】即可直接領取
  • 之家push系統迭代之路

    前言在這個信息爆炸的互聯網時代,能夠及時準確獲取信息是當今社會要解決的關鍵問題之一。隨著之家用戶體量和內容規模的不斷增大,傳統的靠"主動拉"獲取信息的方式已不能滿足用
  • 從零到英雄:高并發與性能優化的神奇之旅

    作者 | 波哥審校 | 重樓作為公司的架構師或者程序員,你是否曾經為公司的系統在面對高并發和性能瓶頸時感到手足無措或者焦頭爛額呢?筆者在出道那會為此是吃盡了苦頭的,不過也得
  • 慕巖炮轟抖音,百合網今何在?

    來源:價值研究所 作者:Hernanderz&ldquo;難道就因為自己的一個產品牛逼了,從客服到總裁,都不愿意正視自己產品和運營上的問題,選擇逃避了嗎?&rdquo;這一番話,出自百合網聯合創
  • 阿里瓴羊One推出背后,零售企業迎數字化新解

    作者:劉曠近年來隨著數字經濟的高速發展,各式各樣的SaaS應用服務更是層出不窮,但本質上SaaS大多局限于單一業務流層面,對用戶核心關切的增長問題等則沒有提供更好的解法。在Saa
  • 小米MIX Fold 3配置細節曝光:搭載領先版驍龍8 Gen2+罕見5倍長焦

    這段時間以來,包括三星、一加、榮耀等等有不少品牌旗下的最新折疊屏旗艦都得到了不少爆料,而小米新一代折疊屏旗艦——小米MIX Fold 3此前也屢屢被傳
  • 英特爾Xe-HP項目終止,將專注Xe-HPC/HPG系列顯卡

    據10 月 31 日消息報道,英特爾高級副總裁兼加速計算系統和圖形事業部總經理 表示,Xe-HP“ Arctic Sound” 系列服務器 GPU 已經應用于 oneAPI devcloud 云服
Top