變數

April 11, 2022

之前介紹各個內建型態時,多半是直接使用實字寫下一個值,實際在撰寫程式時,這麼寫可不行:

print('圓半徑:', 10)
print('圓周長:', 2 * 3.14 * 10)
print('圓面積:', 3.14 * 10 * 10)

半徑 10 同時出現在程式碼中多個位置,圓周率 3.14 也是,如果將來要修改半徑呢?或者是要使用更精確一些的圓周率,像是 3.14159 呢?你得修改多個地方!

建立變數

若能要求 Python 保留某些名稱,可以對照至 10 與 3.14 這些值,每次要運算時,都透過這些名稱取得對應的值,若將名稱對應的值有變化了,既有的程式碼就會取得新的對應值來運算,這樣不是很方便嗎?

radius = 10
PI = 3.14
print('圓半徑:', radius)
print('圓周長:', 2 * PI * radius)
print('圓面積:', PI * radius * radius)

radiusPI 這些名稱稱為變數(Variable),因為它們的對應值是可以變動的,如你所見,程式碼清楚多了,原來 10 這個值是半徑(radius),3.14 是圓周率(PI),而不是魔術數字(Magic number ),而公式的部份,像是 2 * PI * radius,比 2 * 3.14 * 10 有意義的多。

依照型態資訊是記錄在變數之上,或者是執行時期的物件之上,程式語言可以區分為靜態定型(statically-typed)、動態定型(dynamically-typed)語言。

Python 屬於動態定型語言,在執行時期,變數本身沒有型態資訊,建立變數時不用宣告型態,只要命名變數並以指定運算「=」指定對應的值,就建立了一個變數。在建立變數前就嘗試存取某變數,會發生 NameError,表示變數未定義的錯誤。例如:

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

在 Python 中,變數始終是個參考(對應)至值的名稱,指定運算只是改變了變數的參考對象。例如:

>>> x = 1.0
>>> y = x
>>> print(id(x), id(y))
1996838054736 1996838054736
>>> y = 2.0
>>> print(id(x), id(y))
1996838054736 1996838054416
>>>

在上面的範例中,x 一開始參考至 1.0 浮點數物件,而後將 x 參考的物件指定給 y 參考,id 函式能用來取得變數參考的物件記憶體位址值,可以看到一開始 xy 都參考同一物件,之後 y 參考至 2.0,xy 就參考了不同的物件。下面這個例子也顯示,變數在 Python 中只是一個參考:

>>> x = 1
>>> id(x)
1996835088688
>>> x = x + 1
>>> id(x)
1996835088720
>>>

一開始 x 參考至 1整數物件,之後 + 運算後,建立了新的整數物件 2,而後指定給 x,因此 x 參考至新物件。由於變數在 Python 中,只是個參考至物件的名稱,對於可變動物件,才會有以下的操作結果:

>>> x = [1, 2, 3]
>>> y = x
>>> x[0] = 10
>>> y
[10, 2, 3]
>>>

在指定 y = x 時,xy 就參考了同一個物件,將 x[0] 修改為 10,透過 y 就會看到修改的元素。除了使用 id 察看變數參考的物件位址值,以確認兩個變數是否參考同一物件之外,還可以使用 isis not 運算。例如:

>>> list1 = [1, 2, 3]
>>> list2 = [1, 2, 3]
>>> list1 == list2
True
>>> list1 is list2
False
>>>

== 運用在 list 時,可以逐一比較兩個 list 中的元素是否全部相等,因此在上面,list1 == list2 的結果會是 True,然而,list1list2 參考了不同的物件,因此 list1 is list2 的結果會是 False

鴨子定型

變數本身沒有型態,同一個變數可以前後指定不同的資料型態,若想透過變數操作物件的某個方法,只要確認該物件上確實有該方法即可。例如底下 x 前後分別指定了 listtuple,然而兩個物件上,都有可查詢元素索引位置的 index 方法,因此並不會出現錯誤:

>>> x = [1, 2, 3]
>>> x.index(2)
1
>>> x = (10, 20, 30)
>>> x.index(20)
1
>>>

這是動態定型語言界流行的鴨子定型(Duck typing):「如果它走路像個鴨子,游泳像個鴨子,叫聲像個鴨子,那它就是鴨子。」

多型(polymorphism)的角度來看,鴨子定型是次型態多型(subtype polymorphism)的一種形式。

Python 是動態定型語言,然而 Python 3.5 開始,納入了型態提示(Type Hints)特性,可以搭配工具程式,實現靜態型態分析,也就是執行程式前檢查型態方面的錯誤,這之後文件會再談到。 

如果某個變數不再需要,可以使用 del 來刪除它。例如:

>>> x = 10
>>> x
10
>>> del x
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

在其他語言中,可以將變數設定為指定值後無法修改,Python 3.8 以後可以透過型態提示的 Final,結合 mypy 或 IDE 之類的工具來進行靜態時期檢查。 

分享到 LinkedIn 分享到 Facebook 分享到 Twitter