'對於新手來說,Python中有哪些難以理解的概念?'

Python C語言 程序員 電腦 整形 Ubuntu 千鋒python學院 2019-08-15
"

老手都是從新手一路走過來的,很多新手夥伴可能會對一些基礎的概念理解都存在一定的困難,提起Python中難以理解的概念,很多夥伴對於Python變量賦值的機制有些疑惑,不過對於習慣於求根究底的程序員,只有深入理解了某個事物本質,掌握了它的客觀規律,才能得心應手、運用自如,進階更高層次來看待這個事物,此刻“庖丁解牛”這個成語能夠貼切表達這個意思,你看見的是整頭的牛,而我看見的是牛的內部肌理筋骨,就是這個狀態!!!

"

老手都是從新手一路走過來的,很多新手夥伴可能會對一些基礎的概念理解都存在一定的困難,提起Python中難以理解的概念,很多夥伴對於Python變量賦值的機制有些疑惑,不過對於習慣於求根究底的程序員,只有深入理解了某個事物本質,掌握了它的客觀規律,才能得心應手、運用自如,進階更高層次來看待這個事物,此刻“庖丁解牛”這個成語能夠貼切表達這個意思,你看見的是整頭的牛,而我看見的是牛的內部肌理筋骨,就是這個狀態!!!

對於新手來說,Python中有哪些難以理解的概念?

那麼為什麼Python變量賦值的機制難以理解呢?

我想可能是我們的思維被C語言變量賦值的機制所固化了。在C語言中變量所分配到的地址是內存空間中一個固定的位置,當我們改變變量值時,對應內存空間中的值也相應改變。在Python中變量存儲的機制是完全不一樣的,當給一個變量賦值時首先解釋器會給這個值分配內存空間,然後將變量指向這個值的地址,那麼當我們改變變量值的時候解釋器又會給新的值分配另一個內存空間,再將變量指向這個新值的地址,所以和C語言相比,在Python中改變的是變量所指向的地址,而內存空間中的值是固定不變的。

"

老手都是從新手一路走過來的,很多新手夥伴可能會對一些基礎的概念理解都存在一定的困難,提起Python中難以理解的概念,很多夥伴對於Python變量賦值的機制有些疑惑,不過對於習慣於求根究底的程序員,只有深入理解了某個事物本質,掌握了它的客觀規律,才能得心應手、運用自如,進階更高層次來看待這個事物,此刻“庖丁解牛”這個成語能夠貼切表達這個意思,你看見的是整頭的牛,而我看見的是牛的內部肌理筋骨,就是這個狀態!!!

對於新手來說,Python中有哪些難以理解的概念?

那麼為什麼Python變量賦值的機制難以理解呢?

我想可能是我們的思維被C語言變量賦值的機制所固化了。在C語言中變量所分配到的地址是內存空間中一個固定的位置,當我們改變變量值時,對應內存空間中的值也相應改變。在Python中變量存儲的機制是完全不一樣的,當給一個變量賦值時首先解釋器會給這個值分配內存空間,然後將變量指向這個值的地址,那麼當我們改變變量值的時候解釋器又會給新的值分配另一個內存空間,再將變量指向這個新值的地址,所以和C語言相比,在Python中改變的是變量所指向的地址,而內存空間中的值是固定不變的。

對於新手來說,Python中有哪些難以理解的概念?

接下來我們要由淺入深的去驗證下我們的結論。在Ubuntu 16.04 LTS 32 位的環境下通過id方法查看變量的內存地址的方式來進行驗證,為什麼要強調環境呢?因為不同的環境下測試結果可能會由於解釋器的優化不同而有所不同。

那這裡我們就以Python的int類型為例,可以看到執行 i += 1 後,變量i的內存地址會發生變化,事實上 i += 1 並不是在原有變量i的地址上加1,而是重新創建一個值為6的int對象,變量i則引用了這個新的對象,因此當變量i和變量j的值相同時會指向同個內存地址。同樣以Python的float 類型為例也驗證了這個變量存儲管理的機制。

———————— int example————————

i = 5 ——》》》 i ---> 5 id(i) ---> 0xa26f880
i += 1 ——》》》 i ---> 6 id(i) ---> 0xa26f874
j = 5 ——》》》 j ---> 5 id(j) ---> 0xa26f880

________________ float example_______________

i = 1.5 ——》》》 i ---> 1.5 id(i) ---> 0x9e86c8c
i += 1 ——》》》 i ---> 2.5 id(i) ---> 0x9e86cac
j = 1.5 ——》》》 j ---> 1.5 id(j) ---> 0x9e86c8c

陸陸續續的試了列表、字典、字符串、元組等變量類型賦值的效果,我發現其實Python中的對象分為可變類型和不可變類型,列表、字典是可變類型,而整數、浮點、短字符串、元組等是不可變類型。可變類型的變量賦值與我們瞭解的C語言機制相同,而不可變類型的變量賦值時,實際上是重新創建一個不可變類型的對象,並將原來的變量重新指向新創建的對象,當然如果沒有其他變量引用原有對象時,原有對象就會被回收。這也是Python作為動態類型語言的特點,即變量不需要預先聲明類型,當變量在賦值時解釋器會根據值的類型創建對應的內存空間進行存儲,並將變量指向這個地址空間即可,比如運行a=1時,解釋器將變量指向整形值1的地址,當運行a=0.1時,解釋器將變量指向浮點值0.1的地址。

"

老手都是從新手一路走過來的,很多新手夥伴可能會對一些基礎的概念理解都存在一定的困難,提起Python中難以理解的概念,很多夥伴對於Python變量賦值的機制有些疑惑,不過對於習慣於求根究底的程序員,只有深入理解了某個事物本質,掌握了它的客觀規律,才能得心應手、運用自如,進階更高層次來看待這個事物,此刻“庖丁解牛”這個成語能夠貼切表達這個意思,你看見的是整頭的牛,而我看見的是牛的內部肌理筋骨,就是這個狀態!!!

對於新手來說,Python中有哪些難以理解的概念?

那麼為什麼Python變量賦值的機制難以理解呢?

我想可能是我們的思維被C語言變量賦值的機制所固化了。在C語言中變量所分配到的地址是內存空間中一個固定的位置,當我們改變變量值時,對應內存空間中的值也相應改變。在Python中變量存儲的機制是完全不一樣的,當給一個變量賦值時首先解釋器會給這個值分配內存空間,然後將變量指向這個值的地址,那麼當我們改變變量值的時候解釋器又會給新的值分配另一個內存空間,再將變量指向這個新值的地址,所以和C語言相比,在Python中改變的是變量所指向的地址,而內存空間中的值是固定不變的。

對於新手來說,Python中有哪些難以理解的概念?

接下來我們要由淺入深的去驗證下我們的結論。在Ubuntu 16.04 LTS 32 位的環境下通過id方法查看變量的內存地址的方式來進行驗證,為什麼要強調環境呢?因為不同的環境下測試結果可能會由於解釋器的優化不同而有所不同。

那這裡我們就以Python的int類型為例,可以看到執行 i += 1 後,變量i的內存地址會發生變化,事實上 i += 1 並不是在原有變量i的地址上加1,而是重新創建一個值為6的int對象,變量i則引用了這個新的對象,因此當變量i和變量j的值相同時會指向同個內存地址。同樣以Python的float 類型為例也驗證了這個變量存儲管理的機制。

———————— int example————————

i = 5 ——》》》 i ---> 5 id(i) ---> 0xa26f880
i += 1 ——》》》 i ---> 6 id(i) ---> 0xa26f874
j = 5 ——》》》 j ---> 5 id(j) ---> 0xa26f880

________________ float example_______________

i = 1.5 ——》》》 i ---> 1.5 id(i) ---> 0x9e86c8c
i += 1 ——》》》 i ---> 2.5 id(i) ---> 0x9e86cac
j = 1.5 ——》》》 j ---> 1.5 id(j) ---> 0x9e86c8c

陸陸續續的試了列表、字典、字符串、元組等變量類型賦值的效果,我發現其實Python中的對象分為可變類型和不可變類型,列表、字典是可變類型,而整數、浮點、短字符串、元組等是不可變類型。可變類型的變量賦值與我們瞭解的C語言機制相同,而不可變類型的變量賦值時,實際上是重新創建一個不可變類型的對象,並將原來的變量重新指向新創建的對象,當然如果沒有其他變量引用原有對象時,原有對象就會被回收。這也是Python作為動態類型語言的特點,即變量不需要預先聲明類型,當變量在賦值時解釋器會根據值的類型創建對應的內存空間進行存儲,並將變量指向這個地址空間即可,比如運行a=1時,解釋器將變量指向整形值1的地址,當運行a=0.1時,解釋器將變量指向浮點值0.1的地址。

對於新手來說,Python中有哪些難以理解的概念?

但是問題又來了!!!為什麼Python可以這樣肆無忌憚地完成動態類型的特徵?

這裡要深究到PyObject這個結構體的層面。通常來說,無論什麼語言最終被計算機識別到的都是內存中的字節信息,對象實際上就是在更高的層次上把內存上的數據作為一個整體來考慮,比如一個整數或是一個字符串。Python中所有的東西都是對象,它們擁有一些相同的內容,這些內容定義在PyObject這個結構體中。

typedef struct _object {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;

ob_refcnt是一個整形變量,它的作用是實現引用計數機制。比如一個對象A,當有一個新的PyObject *引用該對象時,A的引用計數增加;而當這個PyObject *被刪除時,A的引用計數減少。當A的引用計數減少到0時,A就可以從堆上被刪除,以釋放出內存供別的對象使用。ob_type是一個指向_typeobject結構體的指針,這個結構體實際上也是一個對象,它是用來指定一個對象類型的類型對象,這個類型對象記錄了不同的對象所需的內存空間的大小信息。那麼簡單的說,Python中對象機制的核心一個是引用計數,一個就是類型。

PyObject是一個定長對象的結構體,對於可變長度對象的結構體是PyVarObject,它比PyObject結構體多一個ob_size變量,用於指定容器中包含的元素數量。比如list中有5個元素,那麼PyVarObject.ob_size的值就是5。PyVarObject實際上只是對PyObject的一個擴展而已,任何一個PyVarObject所佔用的內存,開始部分的字節定義和PyObject是一樣的。這就可以解釋說,當Python創建一個整形對象PyIntObject,首先它會為這個對象分配內存,並進行初始化,然後這個對象會由一個PyObject*變量來維護,因為每一個對象都擁有相同的對象頭部,這使得對象的引用變得非常的統一。無論對象實際上的類型是什麼,只需要通過PyObject*指針就可以引用任意的一個對象。

"

老手都是從新手一路走過來的,很多新手夥伴可能會對一些基礎的概念理解都存在一定的困難,提起Python中難以理解的概念,很多夥伴對於Python變量賦值的機制有些疑惑,不過對於習慣於求根究底的程序員,只有深入理解了某個事物本質,掌握了它的客觀規律,才能得心應手、運用自如,進階更高層次來看待這個事物,此刻“庖丁解牛”這個成語能夠貼切表達這個意思,你看見的是整頭的牛,而我看見的是牛的內部肌理筋骨,就是這個狀態!!!

對於新手來說,Python中有哪些難以理解的概念?

那麼為什麼Python變量賦值的機制難以理解呢?

我想可能是我們的思維被C語言變量賦值的機制所固化了。在C語言中變量所分配到的地址是內存空間中一個固定的位置,當我們改變變量值時,對應內存空間中的值也相應改變。在Python中變量存儲的機制是完全不一樣的,當給一個變量賦值時首先解釋器會給這個值分配內存空間,然後將變量指向這個值的地址,那麼當我們改變變量值的時候解釋器又會給新的值分配另一個內存空間,再將變量指向這個新值的地址,所以和C語言相比,在Python中改變的是變量所指向的地址,而內存空間中的值是固定不變的。

對於新手來說,Python中有哪些難以理解的概念?

接下來我們要由淺入深的去驗證下我們的結論。在Ubuntu 16.04 LTS 32 位的環境下通過id方法查看變量的內存地址的方式來進行驗證,為什麼要強調環境呢?因為不同的環境下測試結果可能會由於解釋器的優化不同而有所不同。

那這裡我們就以Python的int類型為例,可以看到執行 i += 1 後,變量i的內存地址會發生變化,事實上 i += 1 並不是在原有變量i的地址上加1,而是重新創建一個值為6的int對象,變量i則引用了這個新的對象,因此當變量i和變量j的值相同時會指向同個內存地址。同樣以Python的float 類型為例也驗證了這個變量存儲管理的機制。

———————— int example————————

i = 5 ——》》》 i ---> 5 id(i) ---> 0xa26f880
i += 1 ——》》》 i ---> 6 id(i) ---> 0xa26f874
j = 5 ——》》》 j ---> 5 id(j) ---> 0xa26f880

________________ float example_______________

i = 1.5 ——》》》 i ---> 1.5 id(i) ---> 0x9e86c8c
i += 1 ——》》》 i ---> 2.5 id(i) ---> 0x9e86cac
j = 1.5 ——》》》 j ---> 1.5 id(j) ---> 0x9e86c8c

陸陸續續的試了列表、字典、字符串、元組等變量類型賦值的效果,我發現其實Python中的對象分為可變類型和不可變類型,列表、字典是可變類型,而整數、浮點、短字符串、元組等是不可變類型。可變類型的變量賦值與我們瞭解的C語言機制相同,而不可變類型的變量賦值時,實際上是重新創建一個不可變類型的對象,並將原來的變量重新指向新創建的對象,當然如果沒有其他變量引用原有對象時,原有對象就會被回收。這也是Python作為動態類型語言的特點,即變量不需要預先聲明類型,當變量在賦值時解釋器會根據值的類型創建對應的內存空間進行存儲,並將變量指向這個地址空間即可,比如運行a=1時,解釋器將變量指向整形值1的地址,當運行a=0.1時,解釋器將變量指向浮點值0.1的地址。

對於新手來說,Python中有哪些難以理解的概念?

但是問題又來了!!!為什麼Python可以這樣肆無忌憚地完成動態類型的特徵?

這裡要深究到PyObject這個結構體的層面。通常來說,無論什麼語言最終被計算機識別到的都是內存中的字節信息,對象實際上就是在更高的層次上把內存上的數據作為一個整體來考慮,比如一個整數或是一個字符串。Python中所有的東西都是對象,它們擁有一些相同的內容,這些內容定義在PyObject這個結構體中。

typedef struct _object {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;

ob_refcnt是一個整形變量,它的作用是實現引用計數機制。比如一個對象A,當有一個新的PyObject *引用該對象時,A的引用計數增加;而當這個PyObject *被刪除時,A的引用計數減少。當A的引用計數減少到0時,A就可以從堆上被刪除,以釋放出內存供別的對象使用。ob_type是一個指向_typeobject結構體的指針,這個結構體實際上也是一個對象,它是用來指定一個對象類型的類型對象,這個類型對象記錄了不同的對象所需的內存空間的大小信息。那麼簡單的說,Python中對象機制的核心一個是引用計數,一個就是類型。

PyObject是一個定長對象的結構體,對於可變長度對象的結構體是PyVarObject,它比PyObject結構體多一個ob_size變量,用於指定容器中包含的元素數量。比如list中有5個元素,那麼PyVarObject.ob_size的值就是5。PyVarObject實際上只是對PyObject的一個擴展而已,任何一個PyVarObject所佔用的內存,開始部分的字節定義和PyObject是一樣的。這就可以解釋說,當Python創建一個整形對象PyIntObject,首先它會為這個對象分配內存,並進行初始化,然後這個對象會由一個PyObject*變量來維護,因為每一個對象都擁有相同的對象頭部,這使得對象的引用變得非常的統一。無論對象實際上的類型是什麼,只需要通過PyObject*指針就可以引用任意的一個對象。

對於新手來說,Python中有哪些難以理解的概念?

深入淺出了Python變量賦值的機制以後,大家就不覺得這是難以理解的概念了吧!

其實學習的樂趣就體現在恍然大悟、融會貫通的那一時刻。

"

相關推薦

推薦中...