# 第一章 快速改造:基礎知識
> 來源:http://www.cnblogs.com/Marlowes/p/5280405.html
> 作者:Marlowes
## 1.1 安裝Python (略······)
安裝Python教程網上能找到很多,這里我不想手打了......
## 1.2 交互式解釋器
當啟動Python的時候,會出現和下面相似的提示:
```
Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec 5 2015, 20:40:30) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
```
_注:不同的版本的提示和錯誤信息可能會有所不同。_
進入Python的交互式解釋器之后輸入下面的命令查看它是否正常的工作:
```
>>> print "Hello,world!"
```
按下回車后,會得到下面的輸出:
```
Hello,world!
```
## 1.3 算法是什么
首先解釋一下什么是_計算機程序設計_。簡單地說,它就是告訴計算機要做什么。計算機可以做很多的事情,但是不太擅長自主思考,程序員要像給小孩喂飯一樣告訴它具體的細節,并且使用計算機能夠理解的語言——算法。“算法”不過是“步驟”或者“食譜”的另外一種說法——對于如何做某事的一份詳細的表述。比如:
SPAM拌SPAM、SPAM、雞蛋和SPAM:
首先,拿一些SPAM;
然后加入一些SPAM、SPAM和雞蛋;
如果喜歡吃特別辣的SPAM,再多加點SPAM;
煮到熟為止——每10分鐘檢查一次。
這個食譜可能不是很有趣,但是它的組成結構還是有些講究的。它包括一系列按順序執行的指令。有些指令可以直接完成(“拿一些SPAM”),有些則需要考慮特定的條件(“如果需要特別辣的SPAM”),還有一些則必須重復次數(“每10分鐘檢查一次”)。
食譜和算法都包括一些材料(對象,物品),以及指令(語句)。本例中,“SPAM”和“雞蛋”就是要素,指令則包括添加SPAM、按照給定的時間烹調,等等。
## 1.4 數字和表達式
交互式Python解釋器可以當作非常強大的計算器使用,如以下的例子:
```
>>> 256868 + 684681
941549
```
以上是非常普通的功能。在絕大多數情況下,常用_算術運算符_的功能和計算器的相同。這里有個潛在的陷阱,就是整數除法(在3.0版本之前的Python是這樣的)。
```
>>> 1 / 2
0
```
從上面的例子可以看出,一個整數(無小數部分的數)被另一個整數除,計算結果的小數部分被截除了,只留下整數部分。有些時候,這個功能很有用,但通常人們只需要普通的除法。有兩個有效的解決方案:要么用實數(包含小數點的數),而不是整數進行運算,要么讓Python改變除法的執行方式。
實數在Python中被稱為_浮點數_(Float,或者Float-point Number),如果參與除法的兩個數中有一個數為浮點數,則運算結果亦為浮點數:
```
>>> 1.0 / 2.0
0.5
>>> 1 / 2.0
0.5
>>> 1 / 2.
0.5
```
如果希望Python只執行普通的除法,那么可以在程序前加上一下語句,或者直接在解釋器里面執行它:
```
>>> from __future__ import division
```
還有另一個方法,如果通過命令行運行Python,可以使用命令開關`-Qnew`。使用上述兩種方法,就可以只執行普通的除法運算:
```
>>> 1/2
0.5
```
當然,單斜線不再用作前面提到的整除了,但是Python提供了另外一個用于實現整除的操作符——雙斜線:
```
>>> 1 // 2
0
```
就算是浮點數,雙斜線也會執行整除:
```
>>> 1.0 // 2.0
0.0
```
現在,已經了解基本的算數運算符了(加、減、乘、除)。除此之外,還有一個非常有用的運算符:
```
>>> 1 % 2
1
```
這是取余(模除)運算符——`x % y`的結果為`x`除以`y`的余數。下面是另外一些例子:
```
>>> 10 / 3
3
>>> 10 % 3
1
>>> 9 / 3
3
>>> 9 % 3 0
>>> 2.75 % 0.5
0.25
```
這里`10/3`得`3`是因為結果被向下取整了。而`3x3=9`,所以相應的余數就是`1`了。在計算`9/3`時,結果就是`3`,沒有小數部分可供截除,因此,余數就是`0`了。如果要進行一些類似文章前面菜譜所述“每10分鐘”檢查一次的操作,那么,取余運算就非常有用了,直接檢查時間`10%`的結果是否為`0`即可。從上述最后一個例子可以看到,取余運算符對浮點數也同樣適用。
最后一個運算符就是冪(乘方)運算符:
```
>>> 2 ** 3
8
>>> -3 ** 2
-9
>>> (-3) ** 2
9
```
_注:冪運算符比取反(一元減運算符)的優先級要高,所以`-3**2`等同于`-(3**2)`。如果想計算`(-3)**2`,就需要顯示說明。_
### 1.4.1 長整數
Python可以處理非常大的整數:
```
>>> 100000000000000000000
100000000000000000000L
```
可以看到數字后面自動加上了一個L
普通的整數不能大于`2 147 483 647`(也不能小于`-2 147 483 648`),如果需要更大的數,可以使用_長整數_。長整數的書寫方法和普通整數一樣,但是結尾有個`L`。(理論上,用小寫的`l`也可以,但是小寫的l看起來太像`1`,所以建議不要用小寫。)
在前面的例子中,Python把整數轉換為了長整數,但是我們自己也可以完成:
```
>>> 100000000000000000000L
100000000000000000000L
```
當然,這只是在不能處理大數的舊版Python中很有用。
也可以對這些龐大的數字進行運算,例如:
```
>>> 165165846835413545L * 2654684351365435434L + 1846846746843
438463188973992663445213017233300373L
```
正如所看到的那樣,長整數和普通整數可以混合使用。在絕大多數情況下,無需擔心長整數和整數的區別,除非需要進行類型檢查。
### 1.4.2 十六進制和八進制
在Python中,十六進制數應該像下面這樣書寫:
```
>>> 0xAF
175
```
而八進制數則是:
```
>>> 010
8
```
十六進制和八進制數的首位數字都是0
## 1.5 變量
_變量_(variable)是另外一個需要熟知的概念。Python中的變量很好理解。變量基本上就是代表(或者引用)某值的名字。舉例來說,如果希望用名字`x`代表`3`,只需要執行下面的語句即可:
```
>>> x = 3
```
這樣的操作成為_賦值_(assignment),數值3被賦值給了變量`x`。或者說:將變量x綁定到了值(或者對象)`3`上面。在變量被賦值之后,就可以在表達式中使用變量。
```
>>> x * 2
6
```
注意,在使用變量之前,需要對其賦值。畢竟不代表任何值的變量沒有什么意義。
_注:變量名可以包括字母、數字和下劃線(`_`)。變量不能以數字開頭,所以`Plan9`是合法變量名,而`9Plan`不是。_
## 1.6 語句
到現在為止,我們一直都在講述表達式,也就是“食譜”的“材料”。那么,語句(也就是指令)是什么呢?
剛才已經介紹了兩類語句:`print`語句和賦值語句。那么語句和表達式之間有什么區別呢?表達式就是_某件事情_,而語句是_做某件事情_(即告訴計算機做什么)。比如`2*2`是`4`,而`print` 2*2`則是打印`4`。那么區別在哪呢?畢竟,它們的行為非常相似。請看下面的例子:
```
>>> 2 * 2
4
>>> print 2 * 2
4
```
如果在交互式解釋器中執行上述兩行代碼,結果是一樣的。但這只是因為交互式解釋器總是把所有表達式的值打印出來而已(都使用了相同的`repr`函數對結果進行呈現,詳細參見1.11.3節)。一般情況下,Python并不會那樣做。在程序中編寫類似`2*2`這樣的表達式并不能做什么有趣的事情(它只計算了`2*2`的結果,但是結果并不會在某處保存或顯示給用戶,它對運算本身之外的東西沒有任何的_副作用_)。另一方面,編寫`print 2*2`則會打印出`4`。
語句和表達式之間的區別在賦值時會表現的更加明顯一些。因為語句不是表達式,所以沒有值可供交互式解釋器打印出來:
```
>>> x = 3
>>>
```
可以看到,下面立刻出現了新的提示符。但是,有些東西已經變化了,`x`現在綁定給了值`3`。
這也是能定義語句的一般性特征:_它們改變了事務_。比如,賦值語句改變了變量,`print`語句改變了屏幕顯示的內容。
賦值語句可能是任何計算機程序設計語言中最重要的語句類型,盡管現在還難以說清它們的重要性。變量就像臨時的“存儲器”(就像烹飪食譜中的鍋碗瓢盆一樣。但是值并沒有保存在變量中——它們保存在計算機內存的深處,被變量引用。隨著本書內容的深入,你會對此越來越清楚:_多個變量可以引用同一個值_。),它的強大之處就在于,在操作變量的時候并不需要知道它們存儲了什么值。比如,即使不知道x和y的值到底是多少,也會知道`x*y`的結果就是`x`和`y`的乘積。所以,可以在程序中通過多種方法來使用變量,而不需要知道在程序運行的時候,最終存儲(或引用)的值到底是什么。
## 1.7 獲取用戶輸入
我們在編寫程序的時候,并不需要知道變量的具體值。當然,解釋器最終還是得知道變量的值。可是,它怎么會知道我們都不知道的事呢?解釋器只知道我們告訴它的內容,對吧?不一定。
事實上,我們通常編寫程序讓別人用,我們無法預測用戶會給程序提供什么樣的值。那么,看看非常有用的`input`函數吧:
```
>>> input("You this year many big?: ")
You this year many big?: 18
18
```
在這里,交互式解釋器執行了第一行的`input(...)`語句。它打印出了字符串`"You this year many big?:"`,并以此作為新的提示符,輸入`18`然后按下回車。`input`語句的結果值就是我輸入的數字,它自動在最后一行被打印出來。這個例子確實不太有用,但是請接著看下面的內容:
```
>>> x = input("x: ")
x: 16
>>> y = input("y: ")
y: 31
>>> print x * y
496
```
Python提示符(`>>>`)后面的語句可以算作一個完整程序的組成部分了,輸入的值(`16`和`31`)有用戶提供,而程序就會打印出輸入的兩個數的乘積`496`。在編寫程序的時候,并不需要知道用戶輸入的數是多少,對吧?
_注:這種做法非常有用,因為你可以將程序存為單獨的文件,以便讓其他用戶可以直接執行。_
管窺:if語句
`if`語句可以讓程序在給定條件為真的情況下執行某些操作(執行另外的語句)。一類條件是使用相等運算符`==`進行相等性測試。是兩個等號,一個等號是用來賦值的。
可以簡單地把這個條件放在if后面,然后用冒號將其和后面的語句隔開:
```
>>> if 1 == 2:
print "One equals two"
...
>>> if 1 == 1:
print "One equals one"
...
One equals one
>>>
```
可以看到,當條件為假(`False`)的時候,什么都沒有發生;當條件為真(`True`)的時候,后面的語句(本例中為`print`語句)被執行。注意,如果在交互式解釋器內使用`if`語句,需要按兩次回車,`if`語句才能執行。(第五章會對其原因進行說明。)
所以,如果變量`time`綁定到當前時間的分鐘數上,那么可以使用下面的語句檢查是不是“到了整點”。
```
>>> if time % 60 == 0:
print "On the hour!"
```
## 1.8 函數
在1.4節中曾經介紹過使用冪運算符(`**`)來計算乘方。事實上,可以用一個_函數_來代替這個運算符,這個函數就是pow:
```
>>> 2 ** 3
8
>>> pow(2, 3)
8
```
函數就像小型程序一樣,可以用來實現特定的功能。Python有很多函數,它們能做很奇妙的事情。你也可以自己定義函數(后面會對此展開講述)。因此,我們通常把`pow`等標準函數稱為_內建函數_。
上例中我使用函數的方式叫做_調用函數_。你可以給它提供_參數_(本例中的2和3)。它會_返回值_給用戶。因為它返回了值,函數調用也可以簡單看作另外一類表達式,就像在本章前面討論的算數表達式一樣(如果忽略了返回值,函數調用也可以看成語句)。事實上,可以結合使用函數調用和運算符來創建更復雜的表達式:
```
>>> 10 + pow(2, 3 * 5) / 3.0
10932.666666666666
```
還有很多像這樣的內建函數可以用于數值表達式。比如使用`abs`函數可以得到數的絕對值,`round`函數則會把浮點數四舍五入為最接近的整數值:
```
>>> abs(-10) 10
>>> round(1.0 / 2.0)
1.0
```
注意最后兩個表達式的區別。整數除法總是會截除結果的小數部分,而`round`函數則會將結果四舍五入為最接近的整數。但是如果想將給定的數值向下取整為某個特定的整數呢?比如一個人的年齡是32.9歲——但是想把它取整為32,因為她還沒到33歲。Python有實現這樣功能的函數(稱為`floor`),但是不能直接使用它。與其他很多有用的函數一樣,你可以在某個_模塊_中找到`floor`函數。
## 1.9 模塊
可以把模塊想象成導入到Python以增強其功能的擴展。需要使用特殊的命令`import`來導入模塊。前面內容提到`floor`函數就在名為`math`的模塊中:
```
>>> import math
>>> math.floor(32.9)
32.0
```
注意它是怎么起作用的:用import導入了模塊,然后按照_“模塊.函數”_的格式使用這個模塊的函數。
如果想把年齡轉換為整數(`32`)而不是浮點數(`32.0`),可以使用`int`函數。(`int`函數/類型把參數轉換成整數時會自動向下取整,所以在轉換過程中,`math.floor`是多余的,可以直接使用`int(32.9)`)
```
>>> int(math.floor(32.9))
32
```
_注:還有類似的函數可以將輸入數轉換為其他類型(比如`long`和`float`)。事實上,他并不完全是普通的函數——它們是類型對象(`type object`)。后面,我將會對類型進行詳述。與`floor`相對的函數是`ceil`(ceiling的簡寫),可以將給定的數值轉換成為大于或等于它的最小整數。_
在確定自己不會導入多個同名函數(從不同模塊導入)的情況下,你可能希望不要在每次調用函數的時候都寫上模塊的名字。那么,可以使用`import`命令的另外一種形式:
```
>>> from math import sqrt
>>> sqrt(9)
3.0
```
在使用了_`from 模塊 import 函數`_這種形式的`import`命令之后,就可以直接使用函數,而不需要模塊名作為前綴。
_注:事實上,可以用變量來引用函數(或者Python之中大多數的對象)。比如,通過 `foo = math.sqrt `進行賦值,然后就可以使用`foo`來計算平方根了:`foo(4)`的結果為`2.0`。_
### 1.9.1 `cmath`和復數
`sqrt`函數用于計算一個數的平方根。看看如果給它一個負數作為參數會如何:
```
>>> from math import sqrt >>> sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
```
或者,在其他平臺會有以下結果:
```
>>> sqrt(-1)
nan
```
_注:`nan`是一個特殊值的簡寫,意思是“not a number” (非數值)。_
這也情有可原,不能求負數的平方根。真的不可以么?其實可以:負數的平方根是虛數(這是標準的數學概念,如果感覺有些繞不過彎來,跳過即可)。那么為什么不能使用`sqrt`?因為它只能處理浮點數,而虛數(以及復數,即實數和虛數之和)是完全不同的。因此,它們由另一個叫做`cmath`(即complex math,復數)的模塊來處理。
```
>>> import cmath
>>> cmath.sqrt(-1)
1j
```
注意,我在這里并沒有使用`from...import...`語句。因為一旦使用了這個語句,就沒法使用普通的`sqrt`函數了。這類命名沖突可能很隱蔽,因此,除非真的需要`from`這個形式的模塊導入語句,否則應該堅持使用普通的`import`。
`1j`是個虛數,虛數均已`j`(或者`J`)結尾,就像長整數使用`L`一樣。我們在這里不深究復數的理論,只舉最后一個例子,來看一下如何使用復數:
```
>>> (1 + 3j) * (9 + 4j)
(-3+31j)
```
可以看到,Python語言本身就提供了對復數的支持。
_注:Python中沒有單獨的虛數類型。它們被看做實數部分為0的復數。_
### 1.9.2 回到 `__future__`
有傳言說Guido van Rossum(Python之父)擁有一架時光機,因為在人們要求增加語言新特性的時候,這個特性通常都已經實現了。當然,我等凡夫俗子是不允許進入這架時光機的。但是Guido很善良,他將時光機的一部分以 `__future__` 這個充滿魔力的模塊的形式融入了Python。通過它可以導入那些在未來會成為標準Python組成部分的新特性。你已經在1.4節見識過這個模塊了,而在本書余下的部分,你還將與它不期而遇。
## 1.10 保存并執行程序
交互式解釋器是Python的強項之一,它讓人們能夠實時檢驗解決方案并且用這門語言做一些實驗。如果想知道如何使用某些語句,那么就試試看吧!但是,在交互式解釋器里面輸入的一切都會在它退出的時候丟失。而我們真正想要的是編寫自己和他人都能運行的程序。在本節中,將會介紹如何實現這一點。
首先,需要一個文本編輯器,最好是專門用來編程的。如果使用Microsoft Word這樣的編輯器(我并不推薦這么做),那么得保證代碼是以純文本形式保存的。如果已經在用IDLE,那么,很幸運:用File→New Windows方式創建一個新的編輯窗口即可。這樣,另外一個窗口就出現了,沒有交互式提示符,很好!
先輸入以下內容:
```
print "Hello, world!"
```
現在選擇File→Save保存程序(其實就是純文本文件)。要確保將程序保存在一個以后能找到的地方。你應該專門建立一個存放Python項目的目錄,還要為自己的程序文件起個有意義的名字。比如`hello.py`。文件名以`.py`結尾是很重要的。
然后就可以使用Edit→Run或者按下Ctrl+F5鍵來運行程序了(如果沒有用IDLE,請查看下一節有關如何在命令提示符下運行程序的內容)。
你會發現`"Hello, world!"`在解釋器窗口內打印出來了,這就是想要的結果。解釋器提示符沒了(不同的版本會有所差異),但是可以按下回車鍵將它找回了(在解釋器窗口按回車鍵)。
接下來,我們對上述腳本進行擴展,如下例所示:
```
name = raw_input("What is your name? ")
print "Hello, " + name + "!"
```
_注:不用管`input`和`raw_input`的區別,稍后,我會進行介紹的。_
如果運行這個程序(記得先保存),應該會在解釋器窗口中看到下面的提示:
```
What is your name?
```
輸入你的名字(比如`XuHoo`),然后按下回車鍵。你將會看到如下內容:
```
Hello, XuHoo!
```
### 1.10.1 通過命令提示符運行Python腳本
事實上,運行程序的方法有很多。首先,假定打開了DOS窗口或者UNIX中的Shell提示符,并且進入了某個包含Python可執行文件(在Windows中是`python.exe`,而UNIX中則是`python`)的目錄,或者包含了這個可執行文件的目錄已經放置在環境變量`PATH`中了(僅適用于Windows)。同時假設,上一節的腳本文件(`hello.py`)也在當前目錄中。那么,可以在Windows中使用以下命令執行來腳本:
```
C:\> python hello.py
```
或者在UNIX下:
```
$ python hello.py
```
可以看到,命令是一樣的,僅僅是系統提示符不同。
_注:如果不想跟什么環境變量打交道,可以直接指定Python解釋器的完整路徑。在Windows中,可以通過以下命令完成操作:_
```
# 根據你的Python版本更改版本號
C:\> C:\Python27\python hello.py
```
### 1.10.2 讓腳本像普通程序一樣運行
有些時候希望像運行其他程序(比如Web瀏覽器、文本編輯器)一樣運行Python程序(也叫做_腳本_),而不需要顯式使用Python解釋器。在UNIX中有個標準的實現方法:在腳本首行前面加上`#!`(叫做pound bang或者shebang),在其后加上用于解釋腳本的程序的絕對路徑(在這里,用于解釋代碼的程序是Python)。即使不太明白其中的原理,如果希望自己的代碼能夠在UNIX下順利執行,那么只要把下面的內容放在腳本的首行即可:
```
#!/usr/bin/env python
```
不管Python的二進制文件在哪里,程序都會自動執行。
_注:在某些操作系統中,如果安裝了最新版的Python,同時舊版的Python仍然存在(因為某些系統程序需要它,所以不能把它卸載),那么在這種情況下,`/usr/bin/env`技巧就不好用了,因為舊版的Python可能會運行程序。因此需要找到新版本Python可執行文件(可能叫做python或python2)的具體位置,然后在pound bang行中使用完整的路徑,如下例所示:_
```
#!/usr/bin/python2
```
_具體的路徑會因系統而異。_
在實際運行腳本之前,必須讓腳本文件具有可執行的屬性(UNIX系統):
```
$ chmod a+x hello.py
```
現在就能這樣運行了(假設當前目錄包含在路徑中):
```
$ hello.py
```
注意如果上述操作不起作用的話,試試`./hello.py`。即當前的目錄(`.`)并不是執行路徑的一部分,這樣的操作也能夠成功。
在Windows系統中,讓代碼像普通程序一樣運行的關鍵在于后綴名`.py`。加入雙擊上一節保存好的`hello.py`文件,如果Python安裝正確,那么,一個DOS窗口就會出現,里面有`"What is your name?"`提示。
然而,像這樣運行程序可能會碰到一個問題:程序運行完畢,窗口也跟著關閉了。也就是說,輸入了名字以后,還沒來得及看結果,程序窗口就已經關閉了。試著改改代碼,在最后加上以下這行代碼:
```
raw_input("Press <enter>")
```
這樣,在運行程序并且輸入名字之后,將會出現一個包含以下內容的DOS窗口:
```
What is your name? XuHoo
Hello, XuHoo!
Press <enter>
```
用戶按下回車鍵以后,窗口就會關閉(因為程序運行結束了)。作為后面內容的預告,現在請你把文件名改為`hello.pyw`(這是Windows專用的文件類型),像剛才一樣雙擊。你會發現什么都沒有!怎么會這樣?在本書后面的內容將會告訴你答案。
### 1.10.3 注釋
井號(`#`)在Python中有些特殊。在代碼中輸入它的時候,它右邊的一切內容都會被忽略(這也是之前Python解釋器不會被`/usr/bin/env`行“卡住”的原因了)。比如:
```
# 打印圓的周長:
print 2 * pi * radius
```
這里的第一行稱為_注釋_。注釋是非常有用的,即為了讓別人能夠更容易理解程序,也為了額你自己回頭再看舊代碼。據說程序員的第一條戒律就是“汝應注釋”(Thou Shalt Comment)(盡管很多刻薄的程序員的座右銘是“如果難寫,就該難讀”)。程序員應該確保注釋說的都是重要的事情,而不是重復代碼中顯而易見的內容。無用的、多余的注釋還不如沒有。例如,下例中的注釋就不好:
```
# 獲得用戶名:
user_name = raw_input("What is your name? ")
```
即使沒有注釋,也應該讓代碼本身易于理解。幸好,Python是一門出色的語言,它能幫助程序員編寫易于理解的程序。
## 1.11 字符串
那么,`raw_input`函數和`"Hello, " + name + "!"`這些內容到底是什么意思?放下`raw_input`函數暫且不表,先來說`"Hello"`這個部分。
本章的第一個程序是這樣的,很簡單:
```
print "Hello, world!"
```
在編程類圖書中,習慣上都會以這樣一個程序作為開篇——問題是我還沒有真正解釋它是怎么工作的。前面已經介紹了`print`語句的基本知識(隨后我會介紹更多相關的內容),但是`"Hello, world!"`是什么呢?是_字符串_(即“一串字符”)。字符串在幾乎所有真實可用的Python程序中都會存在,并且有多種用法,其實最主要的用法就是表示一些文本,比如這個感嘆句`"Hello, world!"`。
### 1.11.1 單引號字符串和轉義引號
字符串是值,就像數字一樣:
```
>>> "Hello, world!"
'Hello, world!'
```
但是,本例中有一個地方可能會讓人覺得吃驚:當Python打印出字符串的時候,是用單引號括起來的,但是我們在程序中用的是雙引號。這有什么卻別嗎?事實上,并沒有區別。
```
>>> 'Hello, world!'
'Hello, world!'
```
這里也用了單引號,結果是一樣的。那么,為什么兩個都可以用呢?因為在某些情況下,它們會排上用場:
```
>>> "Let's go!"
"Let's go!"
>>> '"Hello, world!" she said'
'"Hello, world!" she said'
```
在上面的代碼中,第一段字符串包含了單引號,這時候就不能用單引號將整個字符串括起來了。如果這么做,解釋器會提示錯誤:
```
>>> 'Let's go!'
File "<stdin>", line 1
'Let's go!'
^ SyntaxError: invalid syntax
```
在這里字符串為`'Let'`,Python并不知道如何處理后面的`s`(也就是該行余下的內容)。
在第二個字符串中,句子包含了雙引號。所以,出于之前所述的原因,就需要用單引號把字符串括起來了。或者,并不一定要這樣做,盡管這樣做很直觀。另外一個選擇就是:使用反斜線(`\`)對字符串中的引號進行_轉義_:
```
>>> 'Let\'s go!'
"Let's go!"
```
Python會明白中間的單引號是字符串中的一個字符,而不是字符串的_結束標記_(即便如此,Python也會在打印字符串的時候在最外層使用雙印號)。有的人可能已經猜到,對雙引號也可以使用相同的方式轉義:
```
>>> "\"Hello, world!\" she said"
'"Hello, world!" she said'
```
像這樣轉義引號十分有用,有些時候甚至還是必需的。例如,如果希望打印出一個包含單雙引號的字符串,不用反斜線的話能怎么辦呢?比如字符`'Let\'s say "Hello, world!"'`?
_注:在本章后面的內容中,將會介紹通過使用長字符串和原始字符串(兩者可以聯合使用)來減少絕大多數反斜線的使用。_
### 1.11.2 拼接字符串
繼續探究剛才的例子,我們可以通過另外一種方式輸出同樣的字符串:
```
>>> "Let's say" '"Hello, world!"'
'Let\'s say"Hello, world!"'
```
我只是用一個接著另一個的方式寫了兩個字符串,Python就會自動拼接它們(將它們合為一個字符串)。這種機制用得不多,有時卻非常有用。不過,它們只是在同時寫下兩個字符串時才有效,而且要一個緊接著另一個。否則會出現下面的錯誤:
```
>>> x = "Hello, "
>>> y = "world!"
>>> x y
File "<stdin>", line 1 x y
^ SyntaxError: invalid syntax
```
換句話說,這僅僅是書寫字符串的一種特殊方法,并不是拼接字符串的一般方法。那么,該怎樣拼接字符串呢?就像進行加法運算一樣:
```
>>> "Hello, " + "world!"
'Hello, world!'
>>> x = "Hello, "
>>> y = "world!"
>>> x + y
'Hello, world!'
```
### 1.11.3 字符串表示,str和repr
通過前面的例子讀者們可能注意到了,所有通過Python打印的字符串還是被引號括起來的。這是因為Python打印值的時候會保持該值在Python代碼中的狀態,而不是你希望用戶所看到的狀態。如果使用`print`語句,結果就不一樣了:
```
>>> "Hello, world!"
'Hello, world!'
>>> 1000000L
1000000L
>>> print "Hello, world!"
Hello, world!
>>> print 1000000L
1000000
```
可以看到,長整型數`1 000 000L`被轉換成了數字`1 000 000`,而且在顯示給用戶的時候也如此。但是當你想知道一個變量的值是多少時,可能會對它是整型還是長整型感興趣。
我們在這里討論的實際上是值被轉換為字符串的兩種機制。可以通過以下兩個函數來使用這兩種機制:一種是通過`str`函數,它會把值轉換為合理形式的字符串,以便用戶可以理解;另一種是通過`repr`函數,它會創建一個字符串,以合法的Python表達式的形式來表示值(事實上,`str`和`int`、`long`一樣,是一種類型。而`repr`僅僅是函數)。下面是一些例子:
```
>>> print repr("Hello, world!")
'Hello, world!'
>>> print repr(1000000L)
1000000L
>>> print str("Hello, world!")
Hello, world!
>>> print str(1000000L)
1000000
```
`repr(x)`也可以寫作`` `x` ``實現(注意,`` ` ``是反引號,而不是單引號)。如果希望打印出一個包含數字的句子,那么反引號就很有用了。比如:
```
>>> temp = 42
>>> print "The temperature is " + temp
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
>>> print "The temperature is " + `temp`
The temperature is 42
```
_注:在Python3.0中,已經不再使用反引號了。因此,即使在舊的代碼中看到了反引號,你也應該堅持使用`repr`。_
第一個`print`語句并不能工作,那是因為不可以將字符串和數字進行相加。而第二個則可以正常工作,因為我已經通過反引號將`temp`的值轉換為字符串`"42"`了。(當然,也可以通過`repr`,也可以得到同樣的結果。但是,使用反引號可能更清楚一些。事實上,本例中也可以使用`str`。)
簡而言之,`str`、`repr`和反引號是將Python值轉換為字符串的三種方法。函數`str`讓字符串更易于閱讀,而`repr`(和反引號)則把結果字符串轉換為合法的Python表達式。
### 1.11.4 input和raw_input的比較
相信讀者已經知道`"Hello, " + name + "!"`是什么意思了,那么,`raw_input`函數怎么用呢?`input`函數不夠好嗎?讓我們試一下。在另外一個腳本文件中輸入下面的語句:
```
name = input("What is your name? ")
print "Hello, " + name + "!"
```
看起來這是一個完全合法的程序。但是馬上你就會看到,這樣是不可行的。嘗試運行該程序:
```
What is your name? XuHoo
Traceback (most recent call last):
File "p.py", line 2, in <module>
name = input("What is your name? ")
File "<string>", line 1, in <module>
NameError: name 'XuHoo' is not defined
```
問題在于`input`會假設用戶輸入的是合法的Python表達式(或多或少有些與`repr`函數相反的意思)。如果以字符串作為輸入的名字,程序運行是沒有問題的:
```
What is your name?
"XuHoo" Hello, XuHoo!
```
然而,要求用戶帶著引號輸入他們的名字有點過分,因此,這就需要使用`raw_input`函數,它會把所有的輸入當做原始數據(raw data),然后將其放入字符串中:
```
>>> input("Enter a number: ")
Enter a number: 3
3
>>> raw_input("Enter a number: ")
Enter a number: 3
'3'
```
除非對`input`有特別的需要,否則應該盡可能使用`raw_input`函數。
### 1.11.5 長字符串、原始字符串和`Unicode`
在結束本章之前,還會介紹另外兩種書寫字符串的方法。在需要長達多行的字符串或者包含多種特殊字符的字符串的時候,這些候選的字符串語法就會非常有用。
1.長字符串
如果需要寫一個非常非常長的字符串,它需要跨多行,那么,可以使用三個引號代替普通引號:
```
print '''This is a very long string.
It continues here.
And it's not over yet.
"Hello, world!"
Still here.'''
```
也可以使用三個雙引號,如`"""This is a very long string"""`。注意,因為這種與眾不同的引用方式,你可以在字符串之中同時使用單引號和雙引號,而不需要使用反斜線進行轉義。
注:普通字符串也可以跨行。如果一行之中最后一個字符是反斜線,那么,換行符本身就“轉義”了,也就是被忽略了,例如:
```
>>> print "Hello, \
... world!" Hello, world!
```
這個用法也適用于表達式和語句:
```
>>> 1 + 2 + \
... 4 + 5
12
>>> print \
... "Hello, world!"
Hello, world!
```
2.原始字符串
_原始字符串_對于反斜線并不會特殊對待。在某些情況下這個特性是很有用的(尤其是在書寫正則表達式時候,原始字符串就會特別有用)。在普通字符串中,反斜線有特殊的作用:它會_轉義_,讓你在字符串中加入通常情況下不能直接加入的內容。例如,換行符可以寫為`\n`,并可放于字符串中,如下所示:
```
>>> print "Hello, \nworld!"
Hello,
world!
```
這看起來不錯,但是有時候,這并非是想要的結果。如果希望在字符串中包含反斜線再加上n怎么辦呢?例如,可能需要像DOS路徑`"C:\nowhere"`這樣的字符:
```
>>> path = "C:\nowhere"
>>> path 'C:\nowhere'
```
這看起來是正確的,但是,在打印該字符串的時候就會發現問題了:
```
>>> print path
C:
owhere
```
這并不是期望的結果,那么該怎么辦呢?我可以使用反斜線對其本身進行轉義:
```
>>> print "C:\\nowhere"
C:\nowhere
```
這看起來不錯,但是對于長路徑,那么可能需要很多反斜線:
```
>>> path = "C:\\Program Files\\fnord\\foo\\bar\\frozz\\bozz"
```
在這樣的情況下,原始字符串就派上用場了。原始字符串不會把反斜線當做特殊字符。在原始字符串中輸入的每個字符都會與書寫的方式保持一致:
```
>>> print r"C:\nowhere" C:\nowhere
>>> print r"C:\Program Files\fnord\foo\bar\frozz\bozz"
C:\Program Files\fnord\foo\bar\frozz\bozz
```
可以看到,原始字符串以`r`開頭。看起來可以在原始字符串中放入任何字符,而這種說法也是基本正確的。當然,我們也要像平常一樣對引號進行轉義,但是,最后輸出的字符串包含了轉義所用的反斜線:
```
>>> print r'Let\'s go!'
Let\'s go!
```
不能在原始字符串結尾輸入反斜線。換句話說,原始字符串最后的一個字符不能是反斜線,除非你對反斜線進行轉義(用于轉義的反斜線也會成為字符串的一部分)。參照上一個范例,這是一個顯而易見的結論。如果最后一個字符(位于結束引號前的那個)是反斜線,Python就不清楚是否應該結束字符串:
```
>>> print r"This is illegal\"
File "<stdin>", line 1
print r"This is illegal\"
^ SyntaxError: EOL while scanning string literal
```
好了,這樣還是合理的,但是如果希望原始字符只以一個反斜線作為結尾符的話,那該怎么辦呢?(例如,DOS路徑的最后一個字符有可能是反斜線)好,本節已經告訴了你很多解決此問題的技巧,但本質上就是把反斜線單獨作為一個字符串來處理。以下就是一種簡單的做法:
```
>>> print r"C:\Program Files\foo\bar" "\\"
C:\Program Files\foo\bar\
```
_注:你可以在原始字符串中同時使用單雙引號,甚至三引號字符串也可以_
3\. `Unicode`字符串
字符串常量的最后一種類型就是_`Unicode`字符串_(或者稱為_`Unicode`對象_,與字符串并不是同一個類型)。如果你不知道什么是`Unicode`,那么,可能不需要了解這些內容(如果希望了解更多的信息,可以訪問[`Unicode`的網站](http://www.unicode.org)。Python中的普通字符串在內部是以18位的ASCII碼形成存儲的,而`Unicode`字符串則存儲為16位的`Unicode`字符,這樣就能夠表示更多的字符集了,包括世界上大多數語言的特殊字符。本節不會詳細講述`Unicode`字符串,僅舉一下的例子來說明:
```
>>> u"Hello, world!"
u'Hello, world!'
```
可以看到,`Unicode`字符串使用`u`前綴,就像原始字符串使用`r`一樣。
注:在Python 3.0中,所有字符串都是`Unicode`字符串。
## 1.12 小結
本章講了非常多的內容。在繼續下一章之前,先來看一下本章都學到了什么。
√ 算法。算法是對如何完成一項任務的詳盡描述。實際上,在編寫程序的時候,就是要使用計算機能夠理解的語言(如Python)來描述算法。這類對機器友好的描述就叫做程序,程序主要包含表達式和語句。
√ 表達式。表達式是計算機程序的組成部分,它用于表示值。距離來說,2+2是表達式,表示數值4。簡單的表達式就是通過使用_運算符_(如+或%)和_函數_(如pow)對字面值(比如2或者"Hello")進行處理而構建起來的。通過把簡單的表達式聯合起來可以建立更加復雜的表達式(如(2+2)*(3-1))。表達式也可以包含_變量_。
√ 變量。變量是一個名字,它表示某個值。通過x=2這樣的賦值可以為變量賦予新的值。賦值也是一類_語句_。
√ 語句。語句是告訴計算機做某些事情的指令。它可能涉及改變變量(通過賦值)、向屏幕打印內容(如print "Hello, world!")、導入模塊或者許多其他操作。
√ 函數。Python中的函數就像數學中的函數:它們可以帶有參數,并且返回值(第六章會介紹如何編寫自定義函數)。
√ 模塊。模塊是一些對Python功能的擴展,它可以被導入到Python中。例如,math模塊提供了很多有用的數學函數。
√ 程序。本章之前的內容已經介紹過編寫、保存和運行Python程序的實際操作了。
√ 字符串。字符串非常簡單——就是文本片段,不過,還有很多與字符串相關的知識需要了解。在本章中,你已經看到很多種書寫字符串的方法。第三章將會介紹更多字符串的使用方式。
### 1.12.1 本章的新函數
```
abs(number) 返回數字的絕對值
cmath.sqrt(number) 返回平方根,也可以應用于負數
float(object) 將字符串和數字轉換為浮點數
help() 提供交互式幫助
input(prompt) 獲取用戶輸入
int(object) 將字符串和數字轉換為整數
long(object) 將字符串和數字轉換為長整型數
math.ceil(number) 返回數的上入整數,返回值的類型為浮點數
math.floor(number) 返回數的下入整數,返回值的類型為浮點數
math.sqrt(number) 返回平方根,不適用于負數
pow(x, y[, z]) 返回x的y次方冪(所得結果對z取模)
raw_input(prompt) 獲取用戶輸入,結果被看做原始字符
repr(object) 返回值的字符串表示形式
round(number[, ndigits) 根據給定的精度對數字進行四舍五入
str(object) 將值轉換為字符串
```
### 1.12.2 接下來學什么
表達式的基礎知識已經講解完畢,接下來要探討更高級一點的內容:數據結構。你將學習到如何不再直接和簡單的值(如數字)打交道,而是把它們集中起來處理,存儲在更加復雜的結構中,如列表(list)和字典(dictionary)。另外,我們還將深入了解字符串。在第五章中,將會介紹更多關于語句的知識。之后,編寫漂亮的程序就手到擒來了。