Python基礎(chǔ)手冊(cè)8——Python對(duì)象

一、python 對(duì)象

Python 使用對(duì)象模型來(lái)存儲(chǔ)數(shù)據(jù),構(gòu)造的任何類型的值都是一個(gè)對(duì)象(比如我們創(chuàng)建的整數(shù):26,字符串:“hello world”,列表:[1, 2, 3] 等都是對(duì)象)。對(duì)象可以理解為保存在內(nèi)存中的一段具有固定格式的數(shù)據(jù),所有的 Python 對(duì)象都擁有三個(gè)特性:身份(ID),類型 和 值。

1、身份(ID)

每一個(gè)對(duì)象都有一個(gè)唯一的身份標(biāo)識(shí)自己,對(duì)象一旦建立,它的ID永遠(yuǎn)不會(huì)改變,你可以認(rèn)為它是該對(duì)象在內(nèi)存中的地址。

內(nèi)建函數(shù) id()

內(nèi)建函數(shù) id() 函數(shù)返回一個(gè)表示對(duì)象ID的整數(shù)。

CPython實(shí)現(xiàn)細(xì)節(jié):對(duì)于CPython,id(x)為x存儲(chǔ)在內(nèi)存中的地址。

操作符 is 和 is not

isis not 操作符比較兩個(gè)變量所指向的對(duì)象(或者變量指向的對(duì)象)的ID是否相同,也就是比較兩個(gè)變量是否指向同一個(gè)對(duì)象。



2、類型

每個(gè)對(duì)象的頭部信息中都有一個(gè)類型標(biāo)識(shí)符來(lái)標(biāo)識(shí)這個(gè)對(duì)象的類型(實(shí)際上是一個(gè)指向?qū)?yīng)類型對(duì)象(比如:int、str、dict等)的指針)。對(duì)象的類型決定了對(duì)象數(shù)據(jù)的特性以及支持的操作,還定義了該類型的對(duì)象可能具有的值。

type() 函數(shù)返回對(duì)象的類型(它本身也是一個(gè)對(duì)象)。與ID 一樣,對(duì)象的類型也是不可以修改的。



3、值

某些值可以改變的對(duì)象稱為可變的;一旦建立,值就不可以改變的對(duì)象稱為不可變的。一個(gè)對(duì)象的可變性由它的類型所決定。

注意:上面三個(gè)特性在對(duì)象創(chuàng)建的時(shí)候就被賦值,除了值之外,其它兩個(gè)特性都是只讀的。

4、對(duì)象屬性

某些 Python 對(duì)象有屬性:數(shù)據(jù)或相關(guān)聯(lián)的可執(zhí)行代碼(比如方法)。 Python 用點(diǎn)(.)標(biāo)記法來(lái)訪問(wèn)屬性。屬性包括相應(yīng)對(duì)象的名字等等,最常用的屬性是方法,不過(guò)有一些 Python 類型也有數(shù)據(jù)屬性。含有數(shù)據(jù)屬性的對(duì)象包括(但不限于):類、類實(shí)例、模塊、復(fù)數(shù)和文件。



5、引用計(jì)數(shù)器

每個(gè)對(duì)象的頭部信息中不僅包含了標(biāo)識(shí)該對(duì)象類型的類型標(biāo)識(shí)符,還包含一個(gè)引用的計(jì)數(shù)器,用來(lái)計(jì)數(shù)這個(gè)對(duì)象被變量的引用次數(shù),來(lái)決定是不是可以回收這個(gè)對(duì)象。

你可以向Python查詢對(duì)一個(gè)對(duì)象的引用的次數(shù):在 sys 模塊中的 getrefcount 函數(shù)會(huì)返回對(duì)象的引用次數(shù)。



Python提供了強(qiáng)大的內(nèi)置對(duì)象類型作為語(yǔ)言的組成部分,除非你有內(nèi)置類型無(wú)法提供的特殊對(duì)象要處理,最好總是使用內(nèi)置對(duì)象而不是使用自己的實(shí)現(xiàn)。

Python的內(nèi)置工具是標(biāo)準(zhǔn)的,他們一般都是一致的。對(duì)于簡(jiǎn)單的任務(wù),內(nèi)置類型往往能夠表現(xiàn)問(wèn)題領(lǐng)域的所有結(jié)構(gòu),僅使用Python內(nèi)置對(duì)象類型就能夠完成很多工作。而且Python的內(nèi)置對(duì)象類型優(yōu)化了用C實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)算法。盡管可以實(shí)現(xiàn)屬于自己的類似的數(shù)據(jù)類型,但往往很難達(dá)到內(nèi)置數(shù)據(jù)類型所提供的性能水平。對(duì)于復(fù)雜的任務(wù),或許仍然需要提供自己的對(duì)象,這時(shí)需要使用Python的類或C語(yǔ)言的接口,人工實(shí)現(xiàn)的對(duì)象往往建立在像列表和字典這樣的內(nèi)置類型的基礎(chǔ)上。


二、標(biāo)準(zhǔn)類型(基本數(shù)據(jù)類型)

  • 整型
  • 布爾型
  • 浮點(diǎn)型
  • 復(fù)數(shù)型
  • 字符串
  • 列表
  • 元祖
  • 字典
    這些類型是 Python 內(nèi)建的基本數(shù)據(jù)類型,我們會(huì)在后面的章節(jié)來(lái)詳細(xì)介紹它們。

標(biāo)準(zhǔn)類型的分類

如果讓我們以最啰嗦的方式來(lái)描述標(biāo)準(zhǔn)類型,我們也許會(huì)稱它們是 Python 的 “基本內(nèi)建數(shù)據(jù)對(duì)象原始類型” 。

  • “基本”,是指這些類型都是 Python 提供的標(biāo)準(zhǔn)或核心類型。
  • “內(nèi)建”,是由于這些類型是 Python 默認(rèn)就提供的
  • “數(shù)據(jù)”,因?yàn)樗麄冇糜谝话銛?shù)據(jù)存儲(chǔ)
  • “對(duì)象”,因?yàn)閷?duì)象是數(shù)據(jù)和功能的默認(rèn)抽象
  • “原始”,因?yàn)檫@些類型提供的是最底層的粒度數(shù)據(jù)存儲(chǔ)
  • “類型”,因?yàn)樗麄兙褪菙?shù)據(jù)類型

首先, 我們對(duì)數(shù)據(jù)類型進(jìn)行三個(gè)方面的分類。 Python 提供了高級(jí)的數(shù)據(jù)結(jié)構(gòu),我們需要將那些原始的類型和功能強(qiáng)大的擴(kuò)展類型區(qū)分開(kāi)來(lái)。另外這有助于搞清楚某種類型應(yīng)該具有什么行為。


1、存儲(chǔ)模型

我們對(duì)類型進(jìn)行分類的第一種方式, 就是看看這種類型的對(duì)象能保存多少個(gè)對(duì)象。Python的類型, 就象絕大多數(shù)其它語(yǔ)言一樣,能容納一個(gè)或多個(gè)值。一個(gè)能保存單個(gè)字面對(duì)象的類型我們稱它為原子或標(biāo)量存儲(chǔ)。那些可容納多個(gè)對(duì)象的類型,我們稱之為容器存儲(chǔ)。容器類型又帶來(lái)一個(gè)新問(wèn)題,那就是它是否可以容納不同類型的對(duì)象。所有的 Python 容器對(duì)象都能夠容納不同類型的對(duì)象。

字符串看上去像一個(gè)容器類型,因?yàn)樗鞍弊址ú⑶医?jīng)常多于一個(gè)字符),不過(guò)由于 Python 并沒(méi)有字符類型,所以字符串是一個(gè)自我包含的文字類型。

2、更新模型

另一種對(duì)標(biāo)準(zhǔn)類型進(jìn)行分類的方式就是根據(jù)對(duì)象創(chuàng)建成功之后,它的值可不可以進(jìn)行更新??勺儗?duì)象允許他們的值被更新,而不可變對(duì)象則不允許他們的值被更改。



3、訪問(wèn)模型

根據(jù)訪問(wèn)我們存儲(chǔ)的數(shù)據(jù)的方式對(duì)數(shù)據(jù)類型進(jìn)行分類。在訪問(wèn)模型中共有三種訪問(wèn)方式:直接存取,順序,和映射。

對(duì)非容器類型可以直接訪問(wèn)。所有的數(shù)值類型都?xì)w到這一類。

序列類型是指容器內(nèi)的元素可以按從 0 開(kāi)始的索引順序訪問(wèn)。一次可以訪問(wèn)一個(gè)元素或多個(gè)元素, 也就是大家所了解的切片(slice)。 字符串, 列表和元組都?xì)w到這一類。

映射類型類似序列的索引屬性,不過(guò)它的索引并不使用順序的數(shù)字偏移量取值, 它的元素?zé)o序存放, 通過(guò)一個(gè)唯一的 key 來(lái)訪問(wèn), 這就是映射類型, 它容納的是哈希鍵-值對(duì)的集合。



三、其他內(nèi)建類型

  • 類型
  • None
  • 文件
  • 集合
  • 函數(shù)
  • 模塊

這些是當(dāng)你做 Python 開(kāi)發(fā)時(shí)可能會(huì)用到的一些數(shù)據(jù)類型。我們?cè)谶@里討論 Type 和 None類型的使用,除此之外的其他類型我們會(huì)在后面的單獨(dú)章節(jié)來(lái)詳細(xì)介紹它們。

Python中所有一切都是某種類型的對(duì)象,即便是某個(gè)對(duì)象的類型!任何對(duì)象的類型都是類型為 “type” 的對(duì)象。



1、type 類型對(duì)象

對(duì)象的一系列固有行為和特性(比如支持哪些運(yùn)算,具有哪些方法)必須事先定義好。從這個(gè)角度看,對(duì)象的類型正是保存這些信息的最佳位置。描述一種類型所需要的信息不可能用一個(gè)字符串來(lái)搞定,所以類型不能是一個(gè)簡(jiǎn)單的字符串,這些信息不能也不應(yīng)該和數(shù)據(jù)保存在一起, 所以我們將類型定義成對(duì)象。

通過(guò)調(diào)用 type() 函數(shù)你能夠得到特定對(duì)象的類型信息。

我們得到一個(gè)簡(jiǎn)潔的輸出結(jié)果<class 'int'>。但是它并不是一個(gè)簡(jiǎn)簡(jiǎn)單單的告訴你 123 是個(gè)整數(shù)這樣的字符串。您看到的<class 'int'>實(shí)際上是一個(gè)類型對(duì)象,碰巧它輸出了一個(gè)字符串來(lái)告訴你它是個(gè) int 型對(duì)象。

所有類型對(duì)象的類型都是 type,它也是所有 Python 類型的根和所有 Python 標(biāo)準(zhǔn)類的默認(rèn)元類(metaclass)。

2、None 對(duì)象

Python 有一個(gè)特殊的類型,被稱作 NoneType,它只有一個(gè)值,那就是 None。它用于表示在許多情況下不存在值,一般都用來(lái)起到一個(gè)空的占位符的作用。它不支持任何運(yùn)算也沒(méi)有任何內(nèi)建方法。None 沒(méi)有什么有用的屬性,它的布爾值總是 False。

None不是意味著“未定義”,None是某些內(nèi)容,而不是沒(méi)有內(nèi)容,他是一個(gè)真正的對(duì)象,并且有一塊內(nèi)存,由Python給定一個(gè)內(nèi)置的名稱。


四、內(nèi)部類型

  • 代碼
  • 跟蹤記錄
  • 切片
  • 省略
  • Xrange

我們?cè)谶@里簡(jiǎn)要介紹一下這些內(nèi)部類型,一般的程序員通常不會(huì)直接和這些對(duì)象打交道。
(后期補(bǔ)充 ... )


五、動(dòng)態(tài)類型

python是動(dòng)態(tài)類型的(它自動(dòng)的跟蹤對(duì)象的類型),Python中沒(méi)有類型聲明,運(yùn)行的表達(dá)式的語(yǔ)法(創(chuàng)建對(duì)象時(shí)的表達(dá)式,例如:一個(gè)方括號(hào)的表達(dá)式會(huì)生成一個(gè)列表,大括號(hào)中的表達(dá)式會(huì)建立一個(gè)字典)決定了創(chuàng)建和使用的對(duì)象的類型。一旦創(chuàng)建一個(gè)對(duì)象,它就和操作集合綁定了(只可以對(duì)字符串對(duì)象進(jìn)行字符串相關(guān)的操作,對(duì)列表對(duì)象進(jìn)行列表相關(guān)的操作)所以Python也是強(qiáng)類型語(yǔ)言。

1、變量、對(duì)象和引用

在Python中我們使用對(duì)象模型來(lái)存儲(chǔ)數(shù)據(jù),使用變量(變量名)來(lái)指向我們創(chuàng)建的對(duì)象,我們?cè)诔绦虼a中使用變量名來(lái)引用他們所指向的對(duì)象。我們可以簡(jiǎn)單的認(rèn)為對(duì)象(數(shù)據(jù))就是變量的值,實(shí)際上,變量為對(duì)象的一個(gè)引用。

對(duì)于大多數(shù)編譯型語(yǔ)言來(lái)說(shuō),變量在使用前必須先聲明。但是在 Python 中,無(wú)需顯式的聲明變量,變量在第一次被賦值時(shí)自動(dòng)聲明并創(chuàng)建。變量一旦被賦值,您就可以通過(guò)變量名來(lái)訪問(wèn)它的值,之后的賦值將會(huì)改變變量的值。變量只有被創(chuàng)建和賦值后才能被使用,當(dāng)變量出現(xiàn)在表達(dá)式中,它會(huì)馬上被當(dāng)前引用的對(duì)象所替代。


變量的創(chuàng)建和使用

通過(guò)下面的例子,我們直觀的了解一下變量的創(chuàng)建和使用過(guò)程:

1、創(chuàng)建一個(gè)int類型的對(duì)象代表值4;
2、創(chuàng)建一個(gè)變量 a,如果它還沒(méi)有被創(chuàng)建的話;
3、通過(guò)賦值運(yùn)算符(=)將變量 a 指向?qū)ο?;
4、在表達(dá)式中將變量a替換為對(duì)象4;
5、對(duì)象4和對(duì)象5進(jìn)行加法運(yùn)算,打印運(yùn)算結(jié)果的字符串格式 “9”;

圖示:在運(yùn)行 a = 4 后,變量a變成對(duì)象4 的一個(gè)引用,在內(nèi)部,變量事實(shí)上是到對(duì)象內(nèi)存空間(通過(guò)運(yùn)行常量表達(dá)式 = 4而創(chuàng)建)的一個(gè)指針。一旦變量a被使用,Python自動(dòng)跟隨這個(gè)變量到對(duì)象4的鏈接,使用對(duì)象4參與和對(duì)象5的運(yùn)算。

變量和對(duì)象

1、變量和對(duì)象保存在內(nèi)存的不同部分,并通過(guò)引用(指針)相關(guān)聯(lián);
2、變量總是連接到對(duì)象,并且絕不會(huì)連接到其他變量上,但是更大的對(duì)象可能連接到其他的對(duì)象;
3、在Python內(nèi)部,作為一種優(yōu)化,Python預(yù)先緩存了一些不變的對(duì)象并對(duì)其進(jìn)行復(fù)用;
4、對(duì)象有更復(fù)雜的結(jié)構(gòu),而不僅僅是有足夠的空間表示它的值這么簡(jiǎn)單。每一個(gè)對(duì)象都有兩個(gè)標(biāo)準(zhǔn)的頭部信息:一個(gè)類型標(biāo)識(shí)符去標(biāo)識(shí)這個(gè)對(duì)象的類型;一個(gè)引用的計(jì)數(shù)器,用來(lái)決定是不是可以回收這個(gè)對(duì)象;

2、對(duì)象的動(dòng)態(tài)類型——類型標(biāo)識(shí)符

python是動(dòng)態(tài)類型的,變量名不但無(wú)需事先聲明, 而且也無(wú)需類型聲明。類型的概念僅存在于對(duì)象中而不是變量名中,變量永遠(yuǎn)不會(huì)有任何的和它相關(guān)聯(lián)的類型信息或約束。所以變量是通用的,它只是在一個(gè)特定的時(shí)間點(diǎn),簡(jiǎn)單的引用了一個(gè)特定的對(duì)象而已。

對(duì)象知道自己的類型,每個(gè)對(duì)象都包含了一個(gè)頭部信息——類型標(biāo)識(shí)符(實(shí)際上是一個(gè)指向類型對(duì)象的指針),標(biāo)記了這個(gè)對(duì)象的類型(例如:一個(gè)整數(shù)對(duì)象10,包含了值10以及一個(gè)頭部信息,告訴python,這個(gè)是一個(gè)整數(shù)對(duì)象)。一旦創(chuàng)建一個(gè)對(duì)象,它就和操作集合綁定了(只可以對(duì)字符串對(duì)象進(jìn)行字符串相關(guān)的操作,對(duì)列表對(duì)象進(jìn)行列表相關(guān)的操作),所以Python也是強(qiáng)類型語(yǔ)言。

Python 語(yǔ)言中,對(duì)象的類型和內(nèi)存占用都是運(yùn)行時(shí)確定的,在創(chuàng)建也就是賦值時(shí),解釋器會(huì)根據(jù)運(yùn)行的表達(dá)式的語(yǔ)法和右側(cè)的操作數(shù)(例如:一個(gè)方括號(hào)的表達(dá)式會(huì)生成一個(gè)列表,大括號(hào)中的表達(dá)式會(huì)建立一個(gè)字典)來(lái)決定新對(duì)象的類型。在對(duì)象創(chuàng)建后,一個(gè)該對(duì)象的引用會(huì)被賦值給左側(cè)的變量。

在代碼中檢驗(yàn)特定的類型,會(huì)破壞代碼的靈活性,即限制它只能使用一種類型工作。沒(méi)有這樣的檢測(cè),代碼也許能夠使用整個(gè)范圍的類型工作。在Python中,我們編寫(xiě)對(duì)象接口(所支持的操作)而不是類型。不關(guān)注特定的類型意味著代碼會(huì)自動(dòng)的適應(yīng)他們中的很多類型:任何具有兼容接口的對(duì)象均能夠工作,而不管它是什么對(duì)象類型。動(dòng)態(tài)類型是Python語(yǔ)言靈活性的根源。

3、對(duì)象的垃圾收集——引用計(jì)數(shù)器

在Python中,每當(dāng)一個(gè)變量名被賦予了一個(gè)新的對(duì)象,之前的那個(gè)對(duì)象占用的空間就會(huì)被回收(如果它沒(méi)有被其他的變量名或?qū)ο笏玫脑挘?。這種自動(dòng)回收對(duì)象空間的技術(shù)叫做垃圾收集。

要保持追蹤內(nèi)存中的對(duì)象, Python 使用了引用計(jì)數(shù)這一簡(jiǎn)單技術(shù)。也就是說(shuō) Python 內(nèi)部記錄著所有使用中的對(duì)象各有多少引用。在Python內(nèi)部,它在每個(gè)對(duì)象的頭部信息中保存了一個(gè)引用計(jì)數(shù)器,計(jì)數(shù)器記錄了當(dāng)前指向該對(duì)象的引用的數(shù)目。一旦(并精確的在同一時(shí)間)這個(gè)計(jì)數(shù)器被設(shè)置為零,這個(gè)對(duì)象的內(nèi)存空間就會(huì)被自動(dòng)回收(對(duì)象的空間自動(dòng)放入自由內(nèi)存空間池,等待后來(lái)的對(duì)象使用)。嚴(yán)格來(lái)說(shuō)這不是 100%正確,不過(guò)現(xiàn)階段你可以就這么認(rèn)為。

3.1 增加引用計(jì)數(shù)

當(dāng)對(duì)象被創(chuàng)建并(將其引用)賦值給變量時(shí),該對(duì)象的引用計(jì)數(shù)就被設(shè)置為 1。當(dāng)同一個(gè)對(duì)象(的引用)又被賦值給其它變量時(shí),或作為參數(shù)傳遞給函數(shù), 方法或類實(shí)例時(shí), 或者被賦值為一個(gè)窗口對(duì)象的成員時(shí),該對(duì)象的一個(gè)新的引用,或者稱作別名,就被創(chuàng)建(則該對(duì)象的引用計(jì)數(shù)自動(dòng)加 1)。

請(qǐng)看以下聲明:

x = 3.14
y  = x

語(yǔ)句 x = 3.14 創(chuàng)建了一個(gè)浮點(diǎn)數(shù)對(duì)象(3.14)并將其引用賦值給 x。 x 是其第一個(gè)引用, 因此,該對(duì)象的引用計(jì)數(shù)被設(shè)置為 1。語(yǔ)句 y=x 創(chuàng)建了一個(gè)指向同一對(duì)象的別名 y(參閱圖 3-2)。事實(shí)上并沒(méi)有為 Y 創(chuàng)建一個(gè)新對(duì)象, 而是該對(duì)象的引用計(jì)數(shù)增加了 1 次(變成了 2)。這是對(duì)象引用計(jì)數(shù)增加的方式之一。還有一些其它的方式也能增加對(duì)象的引用計(jì)數(shù), 比如該對(duì)象作為參數(shù)被函數(shù)調(diào)用或這個(gè)對(duì)象被加入到某個(gè)容器對(duì)象當(dāng)中時(shí)。

3.2 減少引用計(jì)數(shù)

當(dāng)對(duì)象的引用被銷毀時(shí),引用計(jì)數(shù)會(huì)減小。最明顯的例子就是當(dāng)引用離開(kāi)其作用范圍時(shí),這種情況最經(jīng)常出現(xiàn)在函數(shù)運(yùn)行結(jié)束時(shí),所有局部變量都被自動(dòng)銷毀,對(duì)象的引用計(jì)數(shù)也就隨之減少。

當(dāng)變量被賦值了另外一個(gè)其他對(duì)象時(shí),原對(duì)象的引用計(jì)數(shù)也會(huì)自動(dòng)減 1:

foo = 'xyz'
bar = foo
foo = 123

當(dāng)字符串對(duì)象"xyz"被創(chuàng)建并賦值給 foo 時(shí), 它的引用計(jì)數(shù)是 1。當(dāng)增加了一個(gè)別名 bar時(shí), 引用計(jì)數(shù)變成了 2。不過(guò)當(dāng) foo 被重新賦值給整數(shù)對(duì)象 123 時(shí), xyz 對(duì)象的引用計(jì)數(shù)自動(dòng)減 1,又重新變成了 1。其它造成對(duì)象的引用計(jì)數(shù)減少的方式包括使用 del 語(yǔ)句刪除一個(gè)變量, 或者當(dāng)一個(gè)對(duì)象被移出一個(gè)窗口對(duì)象時(shí)(或該容器對(duì)象本身的引用計(jì)數(shù)變成了 0 時(shí))。

總結(jié)一下,一個(gè)對(duì)象的引用計(jì)數(shù)在以下情況會(huì)減少:一個(gè)本地引用離開(kāi)了其作用范圍。

3.3 del 語(yǔ)句

del 語(yǔ)句會(huì)刪除對(duì)象的一個(gè)引用,它的語(yǔ)法是:

del obj1[, obj2[,... objN]]

執(zhí)行 del x 刪除該對(duì)象的最后一個(gè)引用, 也就是該對(duì)象的引用計(jì)數(shù)會(huì)減為0, 這會(huì)導(dǎo)致該對(duì)象從此“無(wú)法訪問(wèn)”或“無(wú)法抵達(dá)”。 從此刻起, 該對(duì)象就成為垃圾回收機(jī)制的回收對(duì)象。 注意任何追蹤或調(diào)試程序會(huì)給一個(gè)對(duì)象增加一個(gè)額外的引用, 這會(huì)推遲該對(duì)象被回收的時(shí)間。

3.4 垃圾收集

像上面說(shuō)的,雖然解釋器跟蹤對(duì)象的引用計(jì)數(shù), 但垃圾收集器負(fù)責(zé)釋放內(nèi)存。垃圾收集器是一塊獨(dú)立代碼, 它用來(lái)尋找引用計(jì)數(shù)為 0 的對(duì)象。它也負(fù)責(zé)檢查那些雖然引用計(jì)數(shù)大于 0 但也應(yīng)該被銷毀的對(duì)象。

從技術(shù)上講,Python的垃圾收集主要基于引用計(jì)數(shù)器,然而它也有一部分功能可以及時(shí)的檢測(cè)并回收帶有循環(huán)引用的對(duì)象。由于引用實(shí)現(xiàn)為指針,一個(gè)對(duì)象有可能會(huì)引用自身,或者引用另一個(gè)引用了自身的對(duì)象。 這說(shuō)明只靠引用計(jì)數(shù)是不夠的。 Python 的垃圾收集器實(shí)際上是一個(gè)引用計(jì)數(shù)器和一個(gè)循環(huán)垃圾收集器。 盡管這種情況相對(duì) 很少,由于這樣的對(duì)象的引用計(jì)數(shù)器不會(huì)清除為0,必須特別對(duì)待它們。

這里對(duì)Python的垃圾收集器的介紹只適用于標(biāo)準(zhǔn)的CPython,JPython和IronPython可能使用不同的方案。

垃圾收集最直接的、可感受到的好處就是,這意味著可以在腳本中任意使用對(duì)象而不需要考慮釋放內(nèi)存空間,在程序運(yùn)行時(shí),Python將會(huì)清理那些不在使用的空間。Python 解釋器承擔(dān)了內(nèi)存管理的復(fù)雜任務(wù), 這大大簡(jiǎn)化了應(yīng)用程序的編寫(xiě)。你只需要關(guān)心你要解決的問(wèn)題,至于底層的事情放心交給 Python 解釋器去做就行了。


4、共享引用

在Python中一個(gè)變量可以被賦值引用多個(gè)對(duì)象,也可以多個(gè)變量名引用了同一個(gè)對(duì)象,在Python中這叫作共享引用。

4.1 修改變量的值——變量指向的對(duì)象為不可更改類型

圖示:運(yùn)行賦值語(yǔ)句a=3,在內(nèi)存空間中創(chuàng)建對(duì)象3和變量a,a的引用指向?qū)ο?的內(nèi)存空間。運(yùn)行賦值語(yǔ)句b=a,在內(nèi)存中創(chuàng)建變量b,并且b的引用指向a變量引用指向的對(duì)象3。

圖示:運(yùn)行賦值語(yǔ)句a = "hello",在內(nèi)存空間中創(chuàng)建對(duì)象“hello'”,a的引用指向?qū)ο蟆県ello“的內(nèi)存空間。

在Python中,變量總是一個(gè)指向?qū)ο蟮闹羔?,而不是可改變的?nèi)存區(qū)域的標(biāo)簽:給一個(gè)變量賦一個(gè)新的值,并不是替換了原始的對(duì)象,而是讓這個(gè)變量去引用完全不同的另一個(gè)對(duì)象。

4.2 修改變量的值——變量指向的對(duì)象為可更改類型

圖示:運(yùn)行賦值語(yǔ)句a=[1, 2, 3],在內(nèi)存空間中創(chuàng)建對(duì)象[1, 2, 3]和變量a,a的引用指向?qū)ο骩1, 2, 3]的內(nèi)存空間。運(yùn)行賦值語(yǔ)句b=a,在內(nèi)存中創(chuàng)建變量b,并且b的引用指向a變量引用指向的對(duì)象[1, 2, 3]。運(yùn)行a[0] = "hello",因?yàn)閷?duì)象[1, 2, 3]為可變類型,所以直接修改對(duì)象的值。變量a和變量b指向?qū)ο蟮囊貌蛔儭?/p>

在列表中的元素是通過(guò)他們的位置進(jìn)行讀取的,所以a[0]為對(duì)象1的引用,我們修改a[0]的值,也就是將a[0]的引用指向了其他對(duì)象,并不影響a對(duì)對(duì)象[1, 2 ,3]的引用。

對(duì)于這種可變對(duì)象也就是說(shuō)可在原處直接修改的對(duì)象,共享引用時(shí)需要倍加小心,因?yàn)閷?duì)一個(gè)變量名的修改會(huì)影響到其他的變量。這種行為通常來(lái)說(shuō)就是你所想要的,應(yīng)該了解它是如何運(yùn)作的,讓它按照預(yù)期去工作。這也是默認(rèn)的,如果你不想要這樣的現(xiàn)象發(fā)生,需要Python拷貝對(duì)象,而不是創(chuàng)建引用。有很多拷貝一個(gè)列表的方法,包括內(nèi)置列表函數(shù),以及標(biāo)準(zhǔn)庫(kù)的copy模塊,也許最常用的方法是從頭到尾的分片。

下面這種方式需要認(rèn)真思考(變量c的賦值方式并沒(méi)有將變量c指向變量a指向的對(duì)象,而是創(chuàng)建了一個(gè)新的對(duì)象):

由于Python的引用模型,在Python程序中有兩種不同的方法去檢查是否相等。第一種是 “ == ”操作符,測(cè)試兩個(gè)被引用的對(duì)象是否有相同的值。第二種方法是“ is ”操作符,是在檢查對(duì)象的同一性。如果兩個(gè)變量名精確的指向同一個(gè)對(duì)象,他會(huì)返回True。實(shí)際上,is只是比較實(shí)現(xiàn)引用的指針,所以如果必要的話是代碼中檢測(cè)共享引用的一種辦法。

因?yàn)镻ython緩存并復(fù)用了小的整數(shù)和小的字符串,所以他們并不會(huì)向我們所說(shuō)的被回收。大多數(shù)種類的獨(dú)享都會(huì)在不再引用時(shí)馬上回收,對(duì)于那些不會(huì)被回收的,緩存機(jī)制與代碼并沒(méi)有什么關(guān)系。而且,我們創(chuàng)建兩個(gè)變量賦值相同,他們會(huì)可能會(huì)指向同一個(gè)Python緩存的對(duì)象。

圖示:a和b應(yīng)該是 == 的,但不是 is 的,但是因?yàn)樾〉恼麛?shù)和字符串被緩存并復(fù)用了,所以is表達(dá)式告訴我們a 和b引用了同一個(gè)對(duì)象。

5、作用域

作用域定義一個(gè)代碼塊中變量的可見(jiàn)性。如果一個(gè)局部變量在一個(gè)代碼塊中定義,那么它的作用域就在那個(gè)代碼塊中。如果定義出現(xiàn)在函數(shù)代碼塊中,那么其作用域擴(kuò)展到這個(gè)函數(shù)代碼塊包含的任何代碼塊中,除非某個(gè)被包含的代碼塊為該名稱引入一個(gè)不同的綁定。

當(dāng)一個(gè)變量在代碼塊中使用時(shí),它使用包含它最近的作用域解析。對(duì)于一個(gè)代碼塊所有可見(jiàn)作用域的集合稱做代碼塊的環(huán)境。

當(dāng)一個(gè)變量完全找不到時(shí),將引發(fā)一個(gè) NameError 異常。如果當(dāng)前的作用域是一個(gè)函數(shù)作用域,而且變量引用一個(gè)局部變量,這個(gè)變量在該名稱使用的時(shí)候還沒(méi)有綁定到一個(gè)值,則引發(fā)一個(gè)UnboundLocalError 異常。UnboundLocalError是 NameError 的子類。

如果名稱綁定操作發(fā)生在代碼代碼塊內(nèi)的任何地方,則代碼塊內(nèi)的名稱的所有使用都被視為對(duì)當(dāng)前代碼塊的引用。這可能會(huì)導(dǎo)致在代碼塊中綁定名稱之前出現(xiàn)錯(cuò)誤。這個(gè)規(guī)則是微妙的。Python缺少聲明并允許在代碼塊內(nèi)的任何地方進(jìn)行名稱綁定操作。代碼代碼塊的局部變量可以通過(guò)掃描用于名稱綁定操作的代碼塊的整個(gè)文本來(lái)確定。

如果 global 語(yǔ)句出現(xiàn)在代碼塊內(nèi),在語(yǔ)句中指定的名稱的所有引用都是指該名稱在的頂級(jí)命名空間中的綁定。名稱在頂級(jí)命名空間中的解析通過(guò)搜索全局命名空間,即包含該代碼塊的模塊的命名空間,和內(nèi)建的命名空間——模塊 builtins 的命名空間。首先搜索全局命名空間。如果在那里沒(méi)有找到名稱,則搜索 builtins 命名空間。global語(yǔ)句必須位于該名稱的所有引用之前。

global 語(yǔ)句的作用域與同一代碼塊中的名稱綁定操作相同。如果自由變量的最近的包圍作用域包含全局語(yǔ)句,則該自由變量被視為全局變量。

nonlocal 語(yǔ)句使得對(duì)應(yīng)的名稱引用在最靠近的包含它的函數(shù)的作用域中綁定的變量。如果給定的名稱在任何包含它的函數(shù)的作用域中都找不到,則在編譯時(shí)刻引發(fā) SyntaxError。

模塊的命名空間在第一次導(dǎo)入模塊時(shí)自動(dòng)創(chuàng)建。腳本的主模塊始終叫做 main

類定義以及 exec()eval() 的參數(shù)在名稱解析的上下文中比較特殊。類定義是可以使用和定義名稱的可執(zhí)行語(yǔ)句。這些引用遵循正常的名稱解析規(guī)則,除了一個(gè)例外,就是未綁定的局部變量在全局作用域中查找。類定義的命名空間成為類的屬性字典。在類代碼塊中定義的名稱的作用域限制在類代碼塊中;它不會(huì)延伸到方法的代碼塊中 —— 包括解析式和生成器表達(dá)式,因?yàn)樗鼈兪鞘褂煤瘮?shù)作用域?qū)崿F(xiàn)的。也就是說(shuō)下面這段代碼執(zhí)行會(huì)失?。?/p>

class A: 
    a = 42 
    b = list(a + i for i in range(10))


《Python基礎(chǔ)手冊(cè)》系列:

Python基礎(chǔ)手冊(cè) 1 —— Python語(yǔ)言介紹
Python基礎(chǔ)手冊(cè) 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊(cè) 3 —— Python解釋器
Python基礎(chǔ)手冊(cè) 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊(cè) 5 —— 標(biāo)識(shí)符和關(guān)鍵字
Python基礎(chǔ)手冊(cè) 6 —— 操作符
Python基礎(chǔ)手冊(cè) 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊(cè) 8 —— Python對(duì)象
Python基礎(chǔ)手冊(cè) 9 —— 數(shù)字類型
Python基礎(chǔ)手冊(cè)10 —— 序列(字符串)
Python基礎(chǔ)手冊(cè)11 —— 序列(元組&列表)
Python基礎(chǔ)手冊(cè)12 —— 序列(類型操作)
Python基礎(chǔ)手冊(cè)13 —— 映射(字典)
Python基礎(chǔ)手冊(cè)14 —— 集合
Python基礎(chǔ)手冊(cè)15 —— 解析
Python基礎(chǔ)手冊(cè)16 —— 文件
Python基礎(chǔ)手冊(cè)17 —— 簡(jiǎn)單語(yǔ)句
Python基礎(chǔ)手冊(cè)18 —— 復(fù)合語(yǔ)句(流程控制語(yǔ)句)
Python基礎(chǔ)手冊(cè)19 —— 迭代器
Python基礎(chǔ)手冊(cè)20 —— 生成器
Python基礎(chǔ)手冊(cè)21 —— 函數(shù)的定義
Python基礎(chǔ)手冊(cè)22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊(cè)23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊(cè)24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊(cè)25 —— 裝飾器
Python基礎(chǔ)手冊(cè)26 —— 錯(cuò)誤 & 異常
Python基礎(chǔ)手冊(cè)27 —— 模塊
Python基礎(chǔ)手冊(cè)28 —— 模塊的高級(jí)概念
Python基礎(chǔ)手冊(cè)29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫(xiě)完項(xiàng)目接著寫(xiě)寫(xiě)一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,787評(píng)論 1 118
  • 1.元類 1.1.1類也是對(duì)象 在大多數(shù)編程語(yǔ)言中,類就是一組用來(lái)描述如何生成一個(gè)對(duì)象的代碼段。在Python中這...
    TENG書(shū)閱讀 1,417評(píng)論 0 3
  • 雖然是自己轉(zhuǎn)載的但是是真的好的一篇圖文并茂的對(duì)垃圾回收機(jī)制的講解!!! 先來(lái)個(gè)概述,第二部分的畫(huà)述才是厲害的。 G...
    東皇Amrzs閱讀 119,275評(píng)論 13 175
  • 今天聽(tīng)到了成家的建設(shè)和黑天鵝。對(duì)此,我有一些想法度,我認(rèn)為我之前涂了黑天鵝的一些看法,其實(shí)并不是很成熟。他對(duì)黑天鵝...
    天之巔海無(wú)涯閱讀 266評(píng)論 0 0
  • 文章首發(fā)微信公號(hào)! 昨晚應(yīng)公子要求,在第19期文案訓(xùn)練營(yíng)做了個(gè)分享。 雖然,還是水貨居多,不過(guò)比起第十期的分享,我...
    初美山楂妹閱讀 265評(píng)論 0 0

友情鏈接更多精彩內(nèi)容