【入門必看】不理解「物件」?很可能有致命bug:簡單的Python例子告訴你

PiperNest (同公眾號)發表於2020-11-24

簡介:越來越多的人要在學習工作中用到『程式設計』這個工具了,其中很大一部分人用的是Python。大部分人只是做做簡單的科研計算、繪圖、辦公自動化或者爬蟲,但—— 這就不需要理解「指標與物件導向」了嗎? 在我看來,新手或外行朋友也需要簡單地理解「物件」概念,本文將先演示一個反例(告訴你不理解將多麼容易犯錯誤),然後淺顯地介紹一下「物件」。

致命bug

小垣坐在電腦前,一臉愁容——這麼簡單的程式,她花了3個小時,硬是沒發現哪裡不對,導致程式不通暢。

她發了幾張截圖給我,說“拍老師,發生甚麼事了?”

我一看,嗷,是班花小垣給我發訊息了,我啪一下站起來了,很快啊!

一共兩個.py指令碼檔案,一個八十多行,一個九十多行。整體邏輯沒什麼問題,只是有幾處很“荒唐”,究其原因,是小垣沒有理解「物件」這個概念。

看罷,我拋開這個問題,直接甩給她我騰訊會議ID,開始講解起「物件」這個概念來,因為了解了「物件」這個傳統功夫後,其問題自然而然就化解了。

一個簡單的 numpy 反例

我們以 Python 中最常用的矩陣與代數庫 numpy 為例。

import numpy as np

我們現在定義一個長度為 3 的向量 vec_origin ,其中存放 1,2,3 。然後我們列印一下看看。

vec_origin = np.array([1, 2, 3])
print(vec_origin)

列印結果如下。

[1 2 3]

假如我們需要複製一下向量 vec_origin ,那麼,“自然”是做個等號。如下。然後列印一下。

vec_copy = vec_origin
print(vec_copy)

輸出結果為如下。

[1 2 3]

接下來, vec_originvec_copy 將參與不同的運算任務,其將被加減乘除,還有 賦值 。問題就出在了,如果我們為 vec_copy 賦值,那麼 vec_origin 的值也將被改變。不信你試試下面的程式碼。

print("vec_origin", vec_origin)
print("vec_copy", vec_copy)
vec_copy[0] = 9
print("我們只改變了 vec_copy 的值,但是:")
print("vec_origin", vec_origin)
print("vec_copy", vec_copy)

輸出結果如下。

vec_origin [1 2 3]
vec_copy [1 2 3]
我們只改變了 vec_copy 的值,但是:
vec_origin [9 2 3]
vec_copy [9 2 3]

但是,vec_origin 的值也跟著改變了。這是為什麼呀!

要知道,我們對變數賦值與複製,是不會改變原值的呀!如下。

a = 1
b = a
print(a)  # 1
print(b)  # 1
b = 9
print(a)  # 1
print(b)  # 9

numpy 中的矩陣/向量,是個物件

對於 numpy 來講,我們宣告的矩陣或者向量,並不是一個“數字”那麼簡單,而是一個有「身份證」的『物件』。

一個物件可以有多個名字,但身份證只能有一個。換句話說,身份證才是唯一標識。

對於不是單個數值的數,我們的 = 賦值,只不過是 給物件新增了一個名字罷了。

不信,我們用 python 內建的 id() 函式看看 vec_originvec_copy 名字是不是一樣的。

print(id(vec_origin))
print(id(vec_copy))

結果如下。

2006971817136
2006971817136

很顯然,二者的身份證是一個,這根本就是同一個向量。

對於 listdict 等等,都是同理。

那麼,該如何複製呢?

我的建議是,你沒有必要去可以背誦方法。不同型別的物件有不同賦值方法。對於 numpy 的向量,我們去網際網路搜尋一下就好,檢索詞:numpy 複製

我查到的方法是:使用 np.copy

那麼我們試驗一下。

import numpy as np
vec_origin = np.array([1, 2, 3])
vec_copy = np.copy(vec_origin)  # 這裡複製
print("vec_origin", vec_origin)
print("vec_copy", vec_copy)
vec_copy[0] = 9
print("我們只改變了 vec_copy 的值,但是:")
print("vec_origin", vec_origin)
print("vec_copy", vec_copy)
print(id(vec_origin))
print(id(vec_copy))

結果如下。

vec_origin [1 2 3]
vec_copy [1 2 3]
我們只改變了 vec_copy 的值,但是:
vec_origin [1 2 3]
vec_copy [9 2 3]
2343952817328
2343952817568

如你所見, np.copy 後,是新增了一個物件,而非僅僅是為原物件新增了一個變數名而已。

這是 零基礎程式設計 系列的第一篇文章,之後,我們將用簡單的例子討論:物件導向指標資料結構等等基礎概念。我一直認為,新手對這些概念有些許瞭解後,將少走很多很多的彎路。

最後,你問小垣有沒有改好 bug ,那自然是:傳統功夫點到為止!自從她跟我說她有「物件」後,我就再也沒問過她有沒有理解「物件」。

小垣同學,圖源:GAKKI

祝各位變得更強。歡迎關注公眾號:Piper蛋窩,回覆微信加我微信。歡迎點贊、點選在看鼓勵我。

相關文章