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

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

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

一些範例如下：

   # this is the first comment
   spam = 1  # and this is the second comment
             # ... and now a third!
   text = "# This is not a comment because it's inside quotes."


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

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


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

解释器像一个简单的计算器：你可以输入一个表达式，它将给出结果值。 表达
式语法很直观：运算符 "+", "-", "*" 和 "/" 可被用来执行算术运算；圆括号
("()") 可被用来进行分组。 例如:

   >>> 2 + 2
   4
   >>> 50 - 5*6
   20
   >>> (50 - 5*6) / 4
   5.0
   >>> 8 / 5  # division always returns a floating point number
   1.6

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

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

   >>> 17 / 3  # classic division returns a float
   5.666666666666667
   >>>
   >>> 17 // 3  # floor division discards the fractional part
   5
   >>> 17 % 3  # the % operator returns the remainder of the division
   2
   >>> 5 * 3 + 2  # floored quotient * divisor + remainder
   17

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

   >>> 5 ** 2  # 5 squared
   25
   >>> 2 ** 7  # 2 to the power of 7
   128

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

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

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

   >>> n  # try to access an undefined variable
   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" 类型表示，称为“字符串”）。 这
包括字符 ""!"", 单词 ""rabbit"", 名称 ""Paris"", 句子 ""Got your
back."" 等等. ""Yay! :)""。 它们可以用成对的单引号 ("'...'") 或双引号
(""..."") 来标示，结果完全相同 [2]。

>>> 'spam eggs'  # single quotes
'spam eggs'
>>> "Paris rabbit got your back :)! Yay!"  # double quotes
'Paris rabbit got your back :)! Yay!'
>>> '1975'  # digits and numerals enclosed in quotes are also strings
'1975'

要标示引号本身，我们需要对它进行“转义”，即在前面加一个 "\"。 或者，我
们也可以使用不同类型的引号:

   >>> 'doesn\'t'  # use \' to escape the single quote...
   "doesn't"
   >>> "doesn't"  # ...or use double quotes instead
   "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 means newline
   >>> s  # without print(), special characters are included in the string
   'First line.\nSecond line.'
   >>> print(s)  # with print(), special characters are interpreted, so \n produces new line
   First line.
   Second line.

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

   >>> print('C:\some\name')  # here \n means newline!
   C:\some
   ame
   >>> print(r'C:\some\name')  # note the r before the quote
   C:\some\name

原始字符串还有一个微妙的限制：一个原始字符串不能以奇数个 "\" 字符结束
；请参阅 此 FAQ 条目 了解更多信息及绕过的办法。

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

   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 times 'un', followed by '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'  # can't concatenate a variable and a string literal
     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]  # character in position 0
   'P'
   >>> word[5]  # character in position 5
   'n'

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

   >>> word[-1]  # last character
   'n'
   >>> word[-2]  # second-last character
   'o'
   >>> word[-6]
   'P'

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

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

   >>> word[0:2]  # characters from position 0 (included) to 2 (excluded)
   'Py'
   >>> word[2:5]  # characters from position 2 (included) to 5 (excluded)
   'tho'

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

   >>> word[:2]   # character from the beginning to position 2 (excluded)
   'Py'
   >>> word[4:]   # characters from position 4 (included) to the end
   'on'
   >>> word[-2:]  # characters from the second-last (included) to the end
   '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]  # the word only has 6 characters
   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

也參考:

  文本序列类型 --- str
     字串是 *sequence 型別*的範例之一，並支援該型別常用的操作。

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

  f 字符串
     包含有運算式的字串文本。

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

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


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]  # indexing returns the item
   1
   >>> squares[-1]
   25
   >>> squares[-3:]  # slicing returns a new list
   [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]  # something's wrong here
   >>> 4 ** 3  # the cube of 4 is 64, not 65!
   64
   >>> cubes[3] = 64  # replace the wrong value
   >>> cubes
   [1, 8, 27, 64, 125]

你也可以在通过使用 "list.append()" *方法*，在列表末尾添加新条目（我们
将在后面介绍更多相关的方法）:

   >>> cubes.append(216)  # add the cube of 6
   >>> cubes.append(7 ** 3)  # and the cube of 7
   >>> cubes
   [1, 8, 27, 64, 125, 216, 343]

Python 中的简单赋值绝不会复制数据。 当你将一个列表赋值给一个变量时，该
变量将引用 *现有的列表*。你通过一个变量对列表所做的任何更改都会被引用
它的所有其他变量看到。:

   >>> rgb = ["Red", "Green", "Blue"]
   >>> rgba = rgb
   >>> id(rgb) == id(rgba)  # they reference the same object
   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']
   >>> # clear the list by replacing all the elements with an empty list
   >>> 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 完成比二加二更复杂的任务。 例如，我们可以像下
面这样写出 斐波那契数列 初始部分的子序列:

   >>> # Fibonacci series:
   ... # the sum of two elements defines the next
   ... 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" 在單 ("'...'") 和雙 (""..."") 引號中
    有相同的意思。兩種引號的唯一差別，在於使用單引號時，不需要跳脫 """
    （但必須跳脫 "\'"），反之亦同。
