3. 一個非正式的 Python 簡介
***************************

在下面的例子中，輸入與輸出的區別在於有無提示字元（prompt，*>>>* 和
*...*）：如果要重做範例，你必須在提示字元出現的時候，輸入提示字元後方
的所有內容；那些非提示字元開始的文字行是直譯器的輸出。注意到在範例中，
若出現單行只有次提示字元時，代表該行你必須直接換行；這被使用在多行指令
結束輸入時。

在本手冊中的許多範例中，即便他們為互動式地輸入，仍然包含註解。Python
中的註解 (comments) 由 hash 字元 "#" 開始一直到該行結束。註解可以從該
行之首、空白後、或程式碼之後開始，但不會出現在字串文本 (string
literal) 之中。hash 字元在字串文本之中時仍視為一 hash 字元。因為註解只
是用來說明程式而不會被 Python 解讀，在練習範例時不一定要輸入。

一些範例如下：

   # 這是第一個註解
   spam = 1  # 這是第二個註解
             # ... 現在是第三個！
   text = "# 這不是註解，因為它在引號內。"


3.1. 把 Python 當作計算機使用
=============================

讓我們來試試一些簡單的 Python 指令。啟動直譯器並等待第一個主提示字元
">>>" 出現。（應該不會等太久）


3.1.1. 數字 (Number)
--------------------

直譯器如同一台簡單的計算機：你可以輸入一個 expression（運算式），它會
寫出該式的值。Expression 的語法可以使用：運算子 "+"、"-"、"*" 和 "/"
可以用來執行運算；括號 "()" 可以用來分群。例如：

   >>> 2 + 2
   4
   >>> 50 - 5*6
   20
   >>> (50 - 5*6) / 4
   5.0
   >>> 8 / 5  # 除法總是回傳浮點數
   1.6

整數數字（即 "2"、"4"、"20"）為 "int" 型別，數字有小數點部份的（即
"5.0"、"1.6"）為 "float" 型別。我們將在之後的教學中看到更多數字相關的
型別。

除法 ("/") 永遠回傳一個 float。如果要做 *floor division* 並拿到整數的
結果，你可以使用 "//" 運算子；計算餘數可以使用 "%"：

   >>> 17 / 3  # 傳統除法回傳浮點數
   5.666666666666667
   >>>
   >>> 17 // 3  # 下取整除法捨棄小數部分
   5
   >>> 17 % 3  # % 運算子回傳除法的餘數
   2
   >>> 5 * 3 + 2  # 下取整商 * 除數 + 餘數
   17

在 Python 中，計算冪次 (powers) 可以使用 "**" 運算子 [1]：

   >>> 5 ** 2  # 5 的平方
   25
   >>> 2 ** 7  # 2 的 7 次方
   128

等於符號 ("=") 可以用於為變數賦值。賦值完之後，在下個指示字元前並不會
顯示任何結果：

   >>> width = 20
   >>> height = 5 * 9
   >>> width * height
   900

如果一個變數未被「定義 (defined)」（即變數未被賦值），試著使用它時會出
現一個錯誤：

   >>> n  # 嘗試存取未定義的變數
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: name 'n' is not defined

浮點數的運算有完善的支援，運算子 (operator) 遇上混合的運算元 (operand)
時會把整數的運算元轉換為浮點數：

   >>> 4 * 3.75 - 1
   14.0

在互動式模式中，最後一個印出的運算式的結果會被指派至變數 "_" 中。這表
示當你把 Python 當作桌上計算機使用者，要接續計算變得容易許多：

   >>> tax = 12.5 / 100
   >>> price = 100.50
   >>> price * tax
   12.5625
   >>> price + _
   113.0625
   >>> round(_, 2)
   113.06

這個變數應該被使用者視為只能讀取。不應該明確地為它賦值 --- 你可以創一
個獨立但名稱相同的本地變數來覆蓋掉預設變數和它的神奇行為。

除了 "int" 和 "float"，Python 還支援了其他的數字型別，包含 "Decimal"
和 "Fraction"。Python 亦內建支援複數 (complex numbers)，並使用 "j" 和
"J" 後綴來指定虛數的部份（即 "3+5j"）。


3.1.2. 文字
-----------

Python 可以操作文本（由 "str" 型別表示，即所謂的「字串 (strings)」）和
數字。這包括字元 ""!""、單詞 ""rabbit""、名稱 ""Paris""、句子 ""Got
your back."" 等等。""Yay! :)""。它們可以用單引號 ("'...'") 或雙引號
(""..."") 括起來，會具有相同的結果 [2]。

   >>> 'spam eggs'  # 單引號
   'spam eggs'
   >>> "Paris rabbit got your back :)! Yay!"  # 雙引號
   'Paris rabbit got your back :)! Yay!'
   >>> '1975'  # 以引號包含的數字和數值也是字串
   '1975'

要引用引文，我們需要在其前面加上 "\" 來「跳脫」它。或者我們也可以使用
其他種類的引號：

   >>> 'doesn\'t'  # 用 \' 來跳脫單引號...
   "doesn't"
   >>> "doesn't"  # ...或改用雙引號
   "doesn't"
   >>> '"Yes," they said.'
   '"Yes," they said.'
   >>> "\"Yes,\" they said."
   '"Yes," they said.'
   >>> '"Isn\'t," they said.'
   '"Isn\'t," they said.'

在 Python shell 中，字串定義和輸出字串可能看起來不同。"print()" 函式透
過省略引號並印出跳脫字元和特殊字元來生成更具可讀性的輸出：

   >>> s = 'First line.\nSecond line.'  # \n 表示換行
   >>> s  # 沒有 print()，特殊字元會包含在字串中
   'First line.\nSecond line.'
   >>> print(s)  # 有 print()，特殊字元會被直譯，所以 \n 會產生新的一行
   First line.
   Second line.

如果你不希望字元前出現 "\" 就被當成特殊字元時，可以改使用 *raw string*
，在第一個包圍引號前加上 "r" ：

   >>> print('C:\this\name')  # 這裡 \t 表示 tab，\n 表示換行
   C:      his
   ame
   >>> print(r'C:\this\name')  # 注意引號前的 r
   C:\this\name

原始字串有一個微妙的地方：原始字串可能不會以奇數個 "\" 字元結尾；請參
閱常見問答集來了解更多資訊和解決方法。

字串文本可以跨越數行。其中一方式是使用三個重複引號：""""..."""" 或
"'''...'''"。此時換行字元會被自動加入字串值中，但也可以在換行前加入
"\" 來取消這個行為。在以下的例子中，最初的換行符不會被包含：

   >>> print("""\
   ... Usage: thingy [OPTIONS]
   ...      -h                        Display this usage message
   ...      -H hostname               Hostname to connect to
   ... """)
   Usage: thingy [OPTIONS]
        -h                        Display this usage message
        -H hostname               Hostname to connect to

   >>>

字串可以使用 "+" 運算子連接 (concatenate)，並用 "*" 重複該字串的內容：

   >>> # 3 次 'un'，接著是 'ium'
   >>> 3 * 'un' + 'ium'
   'unununium'

兩個以上相鄰的字串文本（*string literal*，即被引號包圍的字串）會被自動
連接起來：

   >>> 'Py' 'thon'
   'Python'

當你想要分段一個非常長的字串時，兩相鄰字串值自動連接的特性十分有用：

   >>> text = ('Put several strings within parentheses '
   ...         'to have them joined together.')
   >>> text
   'Put several strings within parentheses to have them joined together.'

但這特性只限於兩相鄰的字串值間，而非兩相鄰變數或運算式：

   >>> prefix = 'Py'
   >>> prefix 'thon'  # 無法串接變數和字串字面值
     File "<stdin>", line 1
       prefix 'thon'
              ^^^^^^
   SyntaxError: invalid syntax
   >>> ('un' * 3) 'ium'
     File "<stdin>", line 1
       ('un' * 3) 'ium'
                  ^^^^^
   SyntaxError: invalid syntax

如果要連接變數們或一個變數與一個字串值，使用 "+"：

   >>> prefix + 'thon'
   'Python'

字串可以被「索引 *indexed*」(下標，即 subscripted)，第一個字元的索引值
為 0。沒有獨立表示字元的型別；一個字元就是一個大小為 1 的字串：

   >>> word = 'Python'
   >>> word[0]  # 位置 0 的字元
   'P'
   >>> word[5]  # 位置 5 的字元
   'n'

索引值可以是負的，此時改成從右開始計數：

   >>> word[-1]  # 最後一個字元
   'n'
   >>> word[-2]  # 倒數第二個字元
   'o'
   >>> word[-6]
   'P'

注意到因為 -0 等同於 0，負的索引值由 -1 開始。

除了索引外，字串亦支援「切片 *slicing*」。索引用來拿到單獨的字元，而切
片則可以讓你拿到一個子字串 (substring)：

   >>> word[0:2]  # 從位置 0 (包含) 到 2 (不包含) 的字元
   'Py'
   >>> word[2:5]  # 從位置 2 (包含) 到 5 (不包含) 的字元
   'tho'

切片索引 (slice indices) 有很常用的預設值，省略起點索引值時預設為 0，
而省略第二個索引值時預設整個字串被包含在 slice 中：

   >>> word[:2]   # 從開頭到位置 2 (不包含) 的字元
   'Py'
   >>> word[4:]   # 從位置 4 (包含) 到結尾的字元
   'on'
   >>> word[-2:]  # 從倒數第二個 (包含) 到結尾的字元
   'on'

注意到起點永遠被包含，而結尾永遠不被包含。這確保了 "s[:i] + s[i:]" 永
遠等於 "s"：

   >>> word[:2] + word[2:]
   'Python'
   >>> word[:4] + word[4:]
   'Python'

這裡有個簡單記住 slice 是如何運作的方式。想像 slice 的索引值指著字元們
之間，其中第一個字元的左側邊緣由 0 計數。則 *n* 個字元的字串中最後一個
字元的右側邊緣會有索引值 *n*，例如：

    +---+---+---+---+---+---+
    | P | y | t | h | o | n |
    +---+---+---+---+---+---+
    0   1   2   3   4   5   6
   -6  -5  -4  -3  -2  -1

第一行數字給定字串索引值為 0...6 的位置；第二行則標示了負索引值的位置
。由 *i* 至 *j* 的 slice 包含了標示 *i* 和 *j* 邊緣間的所有字元。

對非負數的索引值而言，一個 slice 的長度等於其索引值之差，如果索引值落
在字串邊界內。例如，"word[1:3]" 的長度是 2。

嘗試使用一個過大的索引值會造成錯誤：

   >>> word[42]  # 這只有六個字母
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   IndexError: string index out of range

然而，超出範圍的索引值在 slice 中會被妥善的處理：

   >>> word[4:42]
   'on'
   >>> word[42:]
   ''

Python 字串無法被改變 --- 它們是 *immutable*。因此，嘗試對字串中某個索
引位置賦值會產生錯誤：

   >>> word[0] = 'J'
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'str' object does not support item assignment
   >>> word[2:] = 'py'
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'str' object does not support item assignment

如果你需要一個不一樣的字串，你必須建立一個新的：

   >>> 'J' + word[1:]
   'Jython'
   >>> word[:2] + 'py'
   'Pypy'

內建的函式 "len()" 回傳一個字串的長度：

   >>> s = 'supercalifragilisticexpialidocious'
   >>> len(s)
   34

也參考:

  Text Sequence Type --- str
     字串是 *sequence 型別*的範例之一，並支援該型別常用的操作。

  字串方法
     字串支援非常多種基本轉換和搜尋的 method（方法）。

  f-string（f 字串）
     包含有運算式的字串文本。

  格式化文字語法
     關於透過 "str.format()" 字串格式化 (string formatting) 的資訊。

  printf-style String Formatting
     在字串為 "%" 運算子的左運算元時，將觸發舊的字串格式化操作，更多的
     細節在本連結中介紹。


3.1.3. List（串列）
-------------------

Python 理解數種複合型資料型別，用來組合不同的數值。當中最多樣變化的型
別為 *list*，可以寫成一系列以逗號分隔的數值（稱之元素，即 item），包含
在方括號之中。List 可以包合不同型別的元素，但通常這些元素會有相同的型
別：

   >>> squares = [1, 4, 9, 16, 25]
   >>> squares
   [1, 4, 9, 16, 25]

如同字串（以及其他內建的 *sequence* 型別），list 可以被索引和切片
(slice)：

   >>> squares[0]  # 索引回傳項目
   1
   >>> squares[-1]
   25
   >>> squares[-3:]  # 切片回傳新串列
   [9, 16, 25]

List 對支援如接合 (concatenation) 等操作：

   >>> squares + [36, 49, 64, 81, 100]
   [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

不同於字串是 *immutable*，list 是 *mutable* 型別，即改變 list 的內容是
可能的：

   >>> cubes = [1, 8, 27, 65, 125]  # 這裡有問題
   >>> 4 ** 3  # 4 的立方是 64，不是 65！
   64
   >>> cubes[3] = 64  # 替換錯誤的值
   >>> cubes
   [1, 8, 27, 64, 125]

你也可以在 list 的最後加入新元素，透過使用 "list.append()" *方法*（我
們稍後會看到更多方法的說明）：

   >>> cubes.append(216)  # 加入 6 的立方
   >>> cubes.append(7 ** 3)  # 以及 7 的立方
   >>> cubes
   [1, 8, 27, 64, 125, 216, 343]

Python 中的簡單賦值永遠不會複製資料。當你將 list 指派給變數時，該變數
會參照*現有 list*。任何透過一個變數對 list 所做的更改都將能夠透過參照
該變數的所有其他變數看到。

   >>> rgb = ["Red", "Green", "Blue"]
   >>> rgba = rgb
   >>> id(rgb) == id(rgba)  # 它們參照到同一物件
   True
   >>> rgba.append("Alph")
   >>> rgb
   ["Red", "Green", "Blue", "Alph"]

所有切片操作都會回傳一個新的 list ，包含要求的元素。這意謂著以下這個切
片回傳了原本 list 的淺複製：

   >>> correct_rgba = rgba[:]
   >>> correct_rgba[-1] = "Alpha"
   >>> correct_rgba
   ["Red", "Green", "Blue", "Alpha"]
   >>> rgba
   ["Red", "Green", "Blue", "Alph"]

也可以對 slice 賦值，這能改變 list 的大小，甚至是清空一個 list：

   >>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
   >>> letters
   ['a', 'b', 'c', 'd', 'e', 'f', 'g']
   >>> # replace some values
   >>> letters[2:5] = ['C', 'D', 'E']
   >>> letters
   ['a', 'b', 'C', 'D', 'E', 'f', 'g']
   >>> # now remove them
   >>> letters[2:5] = []
   >>> letters
   ['a', 'b', 'f', 'g']
   >>> # 透過將所有元素替換為空串列的方式來清空串列
   >>> letters[:] = []
   >>> letters
   []

內建的函式 "len()" 亦可以作用在 list 上：

   >>> letters = ['a', 'b', 'c', 'd']
   >>> len(letters)
   4

也可以嵌套多層 list （建立 list 包含其他 list），例如：

   >>> a = ['a', 'b', 'c']
   >>> n = [1, 2, 3]
   >>> x = [a, n]
   >>> x
   [['a', 'b', 'c'], [1, 2, 3]]
   >>> x[0]
   ['a', 'b', 'c']
   >>> x[0][1]
   'b'


3.2. 初探程式設計的前幾步
=========================

當然，我們可以用 Python 來處理比 2 加 2 更複雜的工作。例如，我們可以印
出費氏數列的首幾項序列：

   >>> # 費波那契數列
   >>> # 以兩個元素的和來決定下一個元素
   >>> a, b = 0, 1
   >>> while a < 10:
   ...     print(a)
   ...     a, b = b, a+b
   ...
   0
   1
   1
   2
   3
   5
   8

這例子引入了許多新的特性。

* 第一行出現了多重賦值：變數 "a" 與 "b" 同時得到了新的值 0 與 1。在最
  後一行同樣的賦值再被使用了一次，示範了等號的右項運算 (expression) 會
  先被計算 (evaluate)，賦值再發生。右項的運算式由左至右依序被計算。

* "while" 迴圈只要它的條件為真（此範例："a < 10"），將會一直重複執行。
  在 Python 中如同 C 語言，任何非零的整數值為真 (true)；零為假 (false)
  。條件可以是字串、list、甚至是任何序列型別；任何非零長度的序列為真，
  空的序列即為假。本例子使用的條件是個簡單的比較。標準的比較運算子
  (comparison operators) 使用如同 C 語言一樣的符號："<"（小於）、">"（
  大於）、"=="（等於）、"<="（小於等於）、">="（大於等於）以及 "!="（
  不等於）。

* 迴圈的主體會*縮排*：縮排在 Python 中用來關連一群陳述式。在互動式提示
  字元中，你必須在迴圈內的每一行一開始鍵入 tab 或者（數個）空白來維持
  縮排。實務上，你會先在文字編輯器中準備好比較複雜的輸入；多數編輯器都
  有自動縮排的功能。當一個複合陳述式以互動地方式輸入，必須在結束時多加
  一行空行來代表結束（因為語法剖析器無法判斷你何時輸入複合陳述的最後一
  行）。注意在一個縮排段落內的縮排方式與數量必須維持一致。

* "print()" 函式印出它接收到引數（們）的值。不同於先前僅我們寫下想要的
  運算（像是先前的計算機範例），它可以處理複數個引數、浮點數數值和字串
  。印出的字串將不帶有引號，並且不同項目間會插入一個空白，因此可以讓你
  容易格式化輸出，例如：

     >>> i = 256*256
     >>> print('The value of i is', i)
     The value of i is 65536

  關鍵字引數 *end* 可以被用來避免額外的換行符加入到輸出中，或者以不同
  的字串結束輸出：

     >>> a, b = 0, 1
     >>> while a < 1000:
     ...     print(a, end=',')
     ...     a, b = b, a+b
     ...
     0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

-[ 註解 ]-

[1] 因為 "**" 擁有較 "-" 高的優先次序，"-3**2" 會被解釋為 "-(3**2)" 並
    得到 "-9"。如果要避免這樣的優先順序以得到 "9"，你可以使用
    "(-3)**2"。

[2] 不像其他語言，特殊符號如 "\n" 在單 ("'...'") 和雙 (""..."") 引號中
    有相同的意思。兩種引號的唯一差別，在於使用單引號時，不需要跳脫 """
    （但必須跳脫 "\'"），反之亦同。
