Python中的變數、對象、引用
來自專欄 小司機的自留地
很多編程語言都有所謂的引用,對象,變數等概念。這些概念在強類型的語言中貌似並不是那麼的重要,但是在動態類型的語言中,還是值得去仔細思考一下的
什麼是變數
對於變數,在數學中我們已經了解過,就是一個值可能會改變的量。在C++中,我們對變數的認識應該相當於一個標識符,創建一個變數,即開闢一塊內存然後給這塊內存起個名字,那麼這個名字就是「變數」,當然,這個變數的含義和內存地址以及指針都是不相同的。
那麼在Python中,變數意味著什麼呢?按照我的理解,一言蔽之,就是一個指針。這確實和之前學過的強類型語言是有不同的。為什麼這麼說呢?首先,python中的變數是沒有類型的,有類型的是「對象」,而不是變數。變數沒有類型,那麼就意味著它可以隨意指向任何對象。在強類型語言中,變數其實都是有具體的類型來限制的,規定一個類型的變數只能被賦值與該類型相同或兼容的值。但是在python中,顯然變數的自由度更大。其次,之前學過體系結構的同學都應該了解,指針的內存空間大小是與類型無關的,其內存空間只是保存了所指向數據的內存地址。之所以說指針也有類型,是因為在計算偏移量的時候,確實需要類型相關的信息。所以,從深層次的含以上來理解,python中的變數與強類型語言中的指針非常相似。
In [1]: a = 3In [2]: a = "abc"In [3]: aOut[3]: abc
從上述代碼中也可以明顯的印證我們上面對python中變數行為的總結是正確的,與指針的行為非常相似。
什麼是對象
我的理解是:對象=確定內存空間+存儲在這塊內存空間中的值。這一點其實和java有些相似。<<java編程思想>>這本書前面第一章,就對對象和引用這兩個概念做了很清晰的區分。Java中,對象是分配在堆上的,存儲真正的數據,而引用是在堆棧中開闢的內存空間用於引用某一個對象。拋開java中引用的概念不談,兩種語言對於對象這個概念的理解,我認為還是可以等價的,都是數據+內存空間感覺。Python中,對象才有類型,不同的對象可以擁有不同類型的數據。
什麼是引用
引用在Python中的語義應該是一種關係,即變數和對象之間的關係,其實也就是指針指向某一塊內存空間的關係。既然是變數和對象之間的關係,那麼其實就意味著,對於一個對象來說,和不同的變數可能存在著多個「引用」關係。因為變數是無類型的,他想關聯誰就可以指向誰,這也就牽扯到了一個「引用計數」的概念,python中的gc大體上就是使用這種原理在做的。
既然說到多個變數可以引用同一個對象,那麼就不得不說一下,如果其中一個引用改變了值,會影響到其他指向這個對象的變數么?
In [4]: a = 2In [5]: b = aIn [6]: aOut[6]: 2In [7]: bOut[7]: 2In [8]: b =3In [9]: aOut[9]: 2In [10]: bOut[10]: 3
從上面的代碼結果來看,答案應該是否定的。如果了解清楚變數和對象的語義就能對這個問題很容易做出判斷,變數值的改變,就相當於指針變數內存空間內值的改變,也就是指向的對象換了一個,但其實之前所指向對象的內容是並沒有收到影響的。
但是也有特例,比如列表,指向列表的變數通過索引訪問的方式,可以直接改動列表在該索引處存儲的值。這個時候,如果有兩個變數同時引用一個列表對象,那麼其中任何一個變數利用這種方式對列表的修改,都會影響到另外一個。
為了能夠避免這種情況,可以對列表單獨拷貝一份。兩個變數互不干涉,而拷貝列表最直接的方式,莫過於「切片」操作了。
變數的比較
對於變數的比較,見得最多的,莫過於「==」和「is」。前者比較的是對象所存儲的數據的值是否相等,後者則比較的是兩個變數是否都引用了同一個對象。這些顯而易見的東西,很多人應該不以為然,但是一些因語言內部優化的小細節,可能會打破你之前的認識。
In [12]: a = 3In [13]: b = 3In [14]: a == bOut[14]: TrueIn [15]: a is bOut[15]: True
按照我們之前所講的,a和b兩個變數應該是引用了兩個不同的對象,但是這兩個對象內存中的值都是3。但是,a is b得到的結果卻是True。原因有如下兩點:
- Python在底層做了一定的優化,對於使用過整數以及字元串都會被緩存起來。所以上述b引用的應該是被緩存過的3
- 之所以採用這種優化的方式,是因為python中數字和字元串一經創建都是不可修改的。所以不會出現,因使用了緩存的對象值造成「臟讀」的問題
結尾
任何一種語言或是一種知識,表面上看起來都是有很多相似的地方的。這也是計算機相關知識的一個比較大的特點。但是很多東西,稍微的往底層走一點,其實還是差別很大的。接觸了c++, golang, java, python這幾種語言,僅僅是在對象,變數,引用這種最基本的概念上還是有挺多細節上的不同的,這也恰恰能夠體現了每種編程語言的設計哲學。所以現在想想,多接觸幾門語言,並不是一件壞事。
推薦閱讀: