關於前一篇大部分都是正確的觀念,除了...
※ 引述《germun (ger)》之銘言:
: ※ 引述《nevikw39 (牧)》之銘言:
: : 如題,Python 的變數系統至今對我仍然是個謎。
: : Python 既是動態語言,變數感覺傾向於強型別但又不需要再宣告。因此,直譯器到底是怎
: : 麼判斷型別的?而且,同一個變數名稱前後可能指涉不同型別。
: 雖然python沒有指標, 但沒有指標觀念的話很容易落入陷阱
: 沒有指標或物件導向觀念的話最好先去懂
: 首先python沒有變數(variable), 只有`name`
: (我不知怎麼翻, 所以以下全稱name)
: 用`賦名`形容比較合適, 只是我們有時還是習慣用變數來稱呼
: 所謂name的意思, 就是等號左邊只是個名稱, 將其名稱指到右邊的物件
: 所有物件的屬性定義全都是依右邊assign的物件來決定,
: 所以才不需事先宣告, 因為物件在建立時已有它的型態和各種資料
: 等號只是將 name 指到該物件, 或反過來說: 給右邊的物件取一個 name
: 例如:
: ==========
: a = 2
: b = 2
: ==========
: 若從C的角度來開, 是將a跟b的值設成2,
: 但從python來看, 任何數字或任何字串本身也都是物件
: 這邊是分別將 a 跟 b 指到 `2` 這個int物件
: 所以 id(a) == id(b) 跟 id(a) == id(2) 會是True, 因為都指到同一物件 `2`
==================================
: 這邊是分別將 a 跟 b 指到 `2` 這個int物件
: 所以 id(a) == id(b) 跟 id(a) == id(2) 會是True, 因為都指到同一物件 `2`
這邊程式碼的意義並不是將a跟b指到"同一個"2
對於literal,Python首先會創造一個object,再把名字bind到object上
literal在Python語言裡只是一個讓你建立一個物件的方式
所以有可能id(a) != id(b) (你可以試一個大於256的數字就知道了)
BUT!!!
為了增進效率,literal可能會有相同的id,例如在這裡,當有名字指到`2`時,
CPython就會把他指到固定的`2`物件,所以這裡的id會一樣
同樣的情況也發生在`True`,`False`等常用的物件上
關於哪些是固定的物件是implementation details
==================================
: 接著再一行
: ===========
: b = 4
: ===========
: 正確的解讀是將 b `重新指向` 4 這個物件
: : 主要想請教的是 dict 和 list 的部分:
: : # base 讀自 json
: : def callback(e):
: : ...
: : data = base
: 從這一行來解讀
: base是已存在的name, 已經有其指向的物件 (舉例來說是個字串"abc"好了)
: 那麼base其實只是個指向"abc"的 name
: 這行的結果就只會是:
: => 新建一個name `data`, 指向`base`所指的物件
: 白話講就是, base指向誰, 那麼data也同樣指向誰
: 也就是:
: base -> "abc"
: data -> "abc"
: 兩者 "abc" 是同記憶體位置的同一物件
: 因而你透過 `data[...] = XXX` 去改動的元素內容, 對應的`base[...]`也會改動
==================================
這邊用字串來做舉例可能不太好:D
因為字串是immutable的,所以技術上來講沒辦法改動物件內容
不過"id會一樣"這件事是確定的
==================================
: 但你若直接:
: ======
: data = "ddd"
: ======
: 這時就會是將data重新指向一個新物件 "ddd", 而不會改動base的內容
: : data[...] = ...
: : 這樣好像會改動到原本 base 的值欸?
: : 還有例如:
: : lst = []
: lst是個物件 `[]`
: : a = [0, 0, 0]
: a也是個物件 `[0, 0, 0]`
: : lst.append(a)
: 把a放入lst
: 此時lst[0] 會指向 a 所指的物件
: id(lst[0]) == id(a) 為 True
: : for i in range(len(a)):
: : a[i] += 1
: a還是同一個list, 記憶體位置不變, 但裡面元素所指的物件都變成1
: 即 a[0], a[1], ... = 1, 1, ...
: 到這時你lst[0]裡面已經跟著變成 [1,1,1], 因為是同一個list
: ps. 要注意這跟 `a = [1,1,1]` 是不同行為, 這是重新建新的list
: : lst.append(a)
: 再一次把a放進去,
: 所以lst[0], lst[1]都是同一個list,
: 一改內容兩個就一起改了
: : 結果 lst 的值不是 [[0, 0, 0], [1, 1, 1]] ,而是 [[1, 1, 1], [1, 1, 1]] 欸!
: : 所以,當我作 data = base 這個運算時,感覺只是將 data 參考指向 base 這個實體而已
: : ,而我若 lst.append(a) 也只是把 lst 的尾端指向 a。那麼,Python 的指派究竟何時是
: : 參考,何時是複製呢?
: 你如果一定要用list的話, 那就一定要先建一個新list
: 例如 b = [], 再把值丟進去
: 或直接複製內容:
: b = a[:]
: 這樣的解讀相當於:
: b = [a[0], a[1], ....]
: 把a的元素分別取出重新建一個list, 如此a跟b就會是不同list
: 甚至是
: a = a[:] # 重新建一個list `a`, 原本的list a遺失,
: 但這邊原本的 a 已經放入`lst`中所以無妨
: 但要注意這邊不會出錯是因為a[0]本身是指向int物件, 因此不會有問題
: 如果 a[0] 指向的是list這類物件, 也就是你的 a 若是二維以上的list,
: 或其他具有屬性的物件, 那又會回到上述狀況了
: 如果你要用到2維以上來計算值, 例如矩陣
: 那麼用numpy array是比較容易
: 不過numpy array跟list又有微妙不同之處, 那又是另一回事了