第2章 變數、表示式和語句

如夢發表於2014-06-20

2.1 值和型別

像字母或數字一樣是程式處理的基本事物之一。目前看到的值有12'Hello, World!'

這些值屬於不同的型別2是整型,'Hello, World!'字串型別,這樣稱呼是因為它包含字母的“串”。可以通過閉合的引號分辨出字串。

如果對值的型別不確定,直譯器會提示你。

>>> type('Hello, World!')
<type 'str'>
>>> type(17)
<type 'int'>

不必驚訝,字串屬於str型別,而且整型屬於int型別。幾乎不明顯的是,點分十進位制數屬於float型別,是因為這些數字以浮點的格式來表示。

>>> type(3.2)
<type 'float'>

'17''3.2'的型別呢?它們看起來像數字,但像字串一樣引在引號中。

>>> type('17')
<type 'str'>
>>> type('3.2')
<type 'str'>

它們是字串。

當輸入一個大的整數時,可能試圖在每三位之間使用逗號進行分組,如1,000,000。在Python中,它不是合法的整型,但它是合法的。

>>> 1,000,000
(1, 0, 0)

嗯,這根本不是我們期望的!Python將1,000,000解釋為逗號分隔的整型序列。它是我們看到的第一個語義錯誤:程式碼可以執行而不產生錯誤訊息,但它做得不對。

2.2 變數

程式語言最強大的功能之一是操作變數的能力。變數是指向值的名稱。

賦值語句會建立變數,並給它們賦值。

>>> message = 'And now for something completely different'
>>> n = 17
>>> pi = 3.1415926535897932

本例建立了三個賦值語句。第一個語句將字串賦給名為message的變數;第二個將整型17賦給n;第三個將π的(大約)值賦給pi。

在論文中表示變數的通常方法是先寫變數名,再使用箭頭指向變數值。這種圖示方法也稱為狀態圖,因為它顯示了每個變數所處的狀態(在心中把它看作變數的狀態)。圖2.1顯示了前一個示例的結果。


圖2.1 狀態圖

圖2.1 狀態圖


變數的型別是其指向值的型別。

>>> type(message)
<type 'str'>
>>> type(n)
<type 'int'>
>>> type(pi)
<type 'float'>

習題 1

如果輸入帶有前導零的整數,可能會出現令人困惑的錯誤:

>>> zipcode = 02492
                  ^
SyntaxError: invalid token

其他的數字好像有效,但結果有些奇怪:

>>> zipcode = 02132
>>> zipcode
1114

為何是這樣?提示:顯示的值是01、010、0100和01000。

2.3 變數名和保留字

程式設計人員通常為變數選擇有意義的名稱——它們記錄了變數的用法。

變數名可以任意長。它們既可以包含字母也可以包含數字,但它們必須以字母開始。使用大寫字母是合法的,但變數名以小寫字母開始是好主意(後面會體會到其中的原委)。

名稱中可以有下劃線(_)。在名稱中,它常常用在多個詞之間,如my_name或者airspeed_of_unladen_swallow

如果使用非法的變數名,就會得到以下語法錯誤。

>>> 76trombones = 'big parade'
SyntaxError: invalid syntax
>>> more@ = 1000000
SyntaxError: invalid syntax
>>> class = 'Advanced Theoretical Zymurgy'
SyntaxError: invalid syntax

76trombones非法是因為它不以字母開頭,more@非法是因為它包含非法字元@。但class錯在哪裡?

因為class是Python的保留字之一。直譯器用保留字來辨識程式的結構,且它們不能用作變數名。

Python 2有31個保留字

and       del       from      not       while    
as        elif      global    or        with     
assert    else      if        pass      yield    
break     except    import    print              
class     exec      in        raise              
continue  finally   is        return             
def       for       lambda    try

在Python 3中,exec不再是保留字,但新加了nonlocal作為保留字。

或許可以將這個保留字的列表放在手邊。如果直譯器對某個變數名產生抱怨,在不知所措時,可以看看它是否出現在這個列表中。

2.4 操作符和運算元

操作符(Operator)是表示如加法或乘法之類運算的特殊符號。用作操作符的值也稱為運算元(operand)。操作符+-*、/和**分別執行加、減、乘、除和冪運算,例如:

20+32   hour-1   hour*60+minute   minute/60   5**2   (5+9)*(15-7)

在其他語言中,^用作冪運算,但在Python中,它是稱為異或(XOR)的位運算操作符。本書將不涉及位運算,但可以在http://wiki.python.org/moin/BitwiseOperators讀到這方面的內容。

在Python 2中,除操作符可能不像預想的那樣:

>>> minute = 59
>>> minute/60
0

minute的值為59,且在常規的數學運算中59除以60得0.98333,而不是0。差異主要是因為Python執行了舍入除法(floor division)。當兩個運算元都是整數時,運算的結果也是整數,舍入除法捨去小數部分,因此在本例中向下舍入為0。

在Python 3中,這種除法的結果為浮點數,新的操作符//執行舍入除法。

如果其中一個運算元是浮點數,Python執行浮點除法,且結果為浮點數。

>>> minute/60.0
0.98333333333333328 

2.5 表示式和語句

表示式(expression)是值、變數和操作符的組合。所有的單個值本身也被認為是表示式,單個變數本身也是如此,因此下面的都是合法的表示式(假設變數x已被賦值)。

17
x
x + 17

語句(statement)是Python直譯器能夠執行程式碼的單位。此前已看到過兩種語句:輸出語句和賦值語句。

從技術上講,表示式也是語句,但它可能更簡單而區別看待。重要的區別在於表示式有值,而語句沒有。

2.6 互動模式和指令碼模式

使用解釋型語言的最大優勢是在把它們放入指令碼之前,可以使用解釋模式測試程式碼。但解釋模式和指令碼模式之間的差異有時也令人迷惑。

例如,如果把Python作為計算器,按如下輸入:

>>> miles = 26.2
>>> miles * 1.61
42.182

第一行將一個值賦給miles,但看不出效果。第二行是一個表示式,因此直譯器進行演算並顯示出結果。因此得到一海里為42千米。

但是,如果在指令碼中輸入同樣的程式碼並執行指令碼,根本得不到輸出。在指令碼模式中,表示式本身無可見的效果。Python實際上演算了表示式,但它只在讓它顯示時才顯示值:

miles = 26.2
print miles * 1.61

這種行為起初有些迷惑。

指令碼通常包含一系列語句。如果多於一個語句,當語句執行時,一次顯示一個結果。

例如,指令碼如下:

 print 1
 x = 2
 print x

輸出如下:

1
2

賦值語句不產生輸出。

習題 2

在Python直譯器中,輸入以下語句,看看它們的結果:

5
x = 5
x + 1

接著,把同樣的語句放到指令碼中執行。輸出又會怎樣?請將指令碼中的每一個表示式轉為輸出語句再執行一次。

2.7 操作的優先順序

當表示式中出現多個操作符時,運算的順序依賴於優先順序規則( rule of precedence)。對於數學操作符,Python遵守數學規則。為便於記住這些規則,使用首字母縮略詞PEMDAS有益的。

  • 括號(P)擁有最高的優先順序,用於按照指定的順序強行表示式的演算。既然括號中的表示式優先運算,2 * (3-1)的結果為4,(1+1)**(5-2)的結果為8。也可以使用括號使表示式更容易閱讀,如在(minute * 100) / 60中,即使括號不改變結果。

  • 冪運算(E)的優先順序次之,因此2**1+1的結果是3,而不是4,且3*1**3的結果是3,而不是27。

  • 乘法(M)除法(D)擁有同樣的優先順序,它們的優先順序高於加法(A)減法(S),而它們也有同樣的優先順序。因此2*3-1的結果為5,而不是4,且6+4/2的結果是8,而不是5。

  • 同樣優先順序的操作符按照從左到右的順序演算(除了冪運算)。因此,在表示式degrees / 2 * pi中,除法先出現且其結果乘以pi。為了除以2 π,可以使用括號或者寫作degrees / 2 / pi

我沒努力去記住其他操作符的優先順序規則。如果表示式看起來不易分辨,我使用括號使它更易讀。

2.8 字串操作符

通常,不能對字串進行數學運算,即使字串看起來像數字,因此下面是非法的:

'2'-'1'    'eggs'/'easy'    'third'*'a charm'

+操作符可以作用於字串,但它可能不如你期望的那樣:它進行連線,意味著將字串從頭到尾連起來。例如:

first = 'throat'
second = 'warbler'
print first + second

程式的輸出是throatwarbler

*操作符也可作用域字串;它執行復制。例如,'Spam'*3的結果為'SpamSpamSpam'。如果其中一個運算元是字串,另一個必須是整型。

+*從加法和乘法上類比是合理的。正如4*3等於4+4+4,可將'Spam'*3類比為'Spam'+'Spam'+'Spam'。另一方面,字串連線和複製與整數加法和乘法也有著重要的不同。你可以想出加法具有的屬性而字串連線不具有嗎?

2.9 註釋

隨著程式越來越大、越來複雜,讀懂它們也變得越加困難。形式語言是密集的,閱讀一段程式碼通常也難以理解它們要做什麼以及為何這樣做。

因此,在程式中新增說明,用自然語言的方式來解釋程式做什麼是個好辦法。這些說明被稱作註釋,它們以#開頭:

# compute the percentage of the hour that has elapsed
percentage = (minute * 100) / 60               

此例中,註釋單獨為一行。也可將註釋放在行尾:

percentage = (minute * 100) / 60     # percentage of an hour                                                                                 

#開始到結尾的所有程式碼都將被忽略--它們在程式中沒有任何作用。

在描述程式碼的非易見性功能時,註釋非常有用。讀者能夠理解程式碼是做什麼的,也就理所當然了;而且它更大的用處是解釋了程式為何這樣做。

這樣的註釋是多餘的,無用的:

v = 5     # assign 5 to v

這樣的註釋包含了有用的資訊,這些資訊是程式碼中所沒有的:

v = 5     # velocity in meters/second.                                                                                           

好的變數名稱可以減少對註釋的需要,但長的變數名也使表示式變得複雜而難以閱讀,因此需要作出權衡。

2.10 除錯

非法的變數名可能是常犯的語法錯誤,比如classyield這類關鍵字或者包含非法字元如odd~jobUS$作為變數名。

如果在變數名中新增了空格,Python會把這作為無操作符的二元運算元:

>>> bad name = 5
SyntaxError: invalid syntax                                                              

對於語法錯誤,錯誤資訊沒有太大的幫助。最常見的資訊是:

SyntaxError: invalid syntax and SyntaxError: invalid token                                                                        

它們也非常不正式。

常犯的執行時錯誤是在“定義前使用”,也就是說,試圖在賦值前使用了某個變數。如果拼錯的變數名,通常會發生此類錯誤:

>>> principal = 327.68
>>> interest = principle * rate
NameError: name 'principle' is not defined                 

變數名稱是大小寫敏感的,因此LaTexlatex並不相同。

常犯的語義錯誤是操作的順序。例如,為了計算1/2π,可能會這樣寫:

>>> 1.0 / 2.0 * pi                                                                

但除法在先,因此得到的是π/2,它們並不相同!對於Python而言它無法知道你所寫的含義,也就不會有錯誤資訊;但就是會得到錯誤的答案。

2.11 詞彙表

值(value): 程式運算元據的基本單位,如數字和字串。

型別(type):數值的型別。目前為止,已經看到的型別有整型(type int)、浮點型(type float)及字串(type str)

相關文章