本章介紹Python的運算符、表達式、程序流程控制語句以及異常處理語句,在這方面,Python和C#是非常類似的,我們僅需要注意它們之間的一些細微差異。另外,在本章我還會簡要介紹Python語言中的兩項有趣功能——列表內(nèi)涵和動態(tài)表達式,雖然它們嚴格來說屬于函數(shù)部分的內(nèi)容,不過我覺得還是放在表達式一章比較合適。
無論使用什么語言,我們編寫的大多數(shù)代碼(邏輯行)都包含表達式。一個表達式可以分解為運算符和操作數(shù),運算符的功能是完成某件事,它們由一些數(shù)學運算符號或者其他特定的關(guān)鍵字表示;運算符需要數(shù)據(jù)來進行運算,這樣的數(shù)據(jù)被稱為操作數(shù)。例如,2 + 3是一個簡單的表達式,其中+是運算符,2和3是操作數(shù)。
算術(shù)運算符是程序設(shè)計語言最基本的運算符。Python提供的算術(shù)運算符除了+、-、*、/、%(求余)之外,還提供了兩種C#中未提供的運算符:求冪(**)和取整除(//)。下面我們通過一段代碼來理解這兩個算術(shù)運算符:
1 #-*-coding:utf-8-*-
2 x = 3.3
3 y = 2.2
4 a = x**y
5 print a
6 #輸出13.827086118,即3.3的2.2次冪,在C#中可用Pow方法實現(xiàn)冪運算
7 b = x//y
8 print b
9 #輸出1.0,取整除返回商的整數(shù)部分
10 c = x/y
11 print c
12 #輸出1.5,注意體會普通除與取整除的區(qū)別
賦值就是給一個變量賦一個新值,除了簡單的 = 賦值之外,Python和C#都支持復(fù)合賦值,例如x += 5,等價于 x = x + 5。
Python不支持C#中的自增和自減運算符,例如x++這種語句在Python中會被提示語法錯誤。C#程序員可能用慣了這種表達方式(要不為什么叫C++++呢),在Python中,請老老實實的寫x += 1就是了。
Python的邏輯運算符與C#有較大區(qū)別,Python用關(guān)鍵字and、or、not代替了C#語言中的邏輯運算符&&、||和! ,此外Pyhton中參與邏輯運算的操作數(shù)不限于布爾類型,任何類型的值都可以參與邏輯運算,參見1.2.2節(jié)(布爾類型)的討論。
用邏輯運算符將操作數(shù)或表達式連接起來就是邏輯表達式。與C#一樣,Python中的邏輯表達式是“短路”執(zhí)行的,也就是說只有需要時才會進行邏輯表達式右邊值的計算,例如表達式 a and b 只有當a為True時才計算b。思考一下,if (0 and 10/0): 這條語句會引發(fā)除數(shù)為零的異常嗎?
此外還要注意:在Python 中,and 和 or 所執(zhí)行的邏輯運算并不返回布爾值,而是返回它們實際進行比較的值之一。下邊是一個例子:
1 print 'a' and 'b'
2 #輸出b
3 print '' and 'b'
4 #輸出空串
關(guān)系運算實際上是邏輯運算的一種,關(guān)系表達式的返回值總是布爾值。Python中的比較操作符與C#是完全一樣的,包括==、!=、>、<、>=和<=共6種。
除了基本的變量比較外,Python的關(guān)系運算符還包括身份運算符is。在Python中,is用來檢驗兩個對象在內(nèi)存中是否指向同一個對象(還記得“一切數(shù)據(jù)皆對象嗎,一切命名皆引用”嗎?)。注意Python中is的含義和C#有所不同,在C#中,is 操作符被用于動態(tài)地檢查運行時對象類型是否和給定的類型兼容。例如,運算e is T ,其中e 是一個對象,T 是一個類型,返回值是一個布爾值,它表示e是否能轉(zhuǎn)換于 T 類型。
三元運算符是C/C++/C#一系語言所特有的一類運算符,例如,對表達式b? x: y,先計算條件b,然后進行判斷,如果b的值為true,則計算并返回x的值,否則計算并返回y的值。
在Python中,提供了專門的邏輯分支表達式來模擬C系中的三元運算,我們也可以在一行語句中完成三元運算,例如
print '偶數(shù)' if x % 2 == 0 else '奇數(shù)'
你期待我列一個很長的表嘛?自己去查好伐,我就不浪費博客園的存儲空間了??傊痪湓挘焊悴磺宄美ㄌ枺?/p>
Python用if,elif,else三個關(guān)鍵字進行條件判斷,與C#唯一的區(qū)別就是用elif取代了else if,少打兩個字,其它都一樣。此外別忘了在if等語句后加 : 哦!
如果一個流程控制分支下不做任何事情,記得寫一句pass語句,不然Python會報錯。例如:
1 if 0:
2 pass #神經(jīng)??!這種例子用來說明什么?
在Python中沒有switch語句,你可以使用if..elif..else語句來完成同樣的工作。如果你覺得繁瑣,可以試試dict實現(xiàn)方式,下邊是個例子,分別對比了兩種實現(xiàn)方式。
1 # 類C#偽碼,根據(jù)輸入的不同參數(shù)選擇程序的不同行為
2 switch(x):
3 case "1":
4 print 'one'; break;
5 case "2":
6 print 'two'; break;
7 default:
8 print 'nothing!'
9
10 # 使用 if 替代
11 if x =='1':
12 print 'one'
13 elif x=='2':
14 print 'two'
15 else:
16 print 'nothing!'
17
18 # 使用dict
19 numtrans = {
20 1: 'one',
21 2: 'two',
22 ...
23 }
24 try:
25 print numtrans[x]
26 except KeyError:
27 print 'nothing!'
28
29 # 也可以在分支中使用方法(函數(shù))
30 def print_one():
31 print 'one'
32 def print_two():
33 print 'two'
34 numtrans = {
35 1:print_one,
36 2:print_two,
37 }
38
39 try:
40 numtrans[x]() #注意名字+括號就可以執(zhí)行方法了,這個實際上很牛X的。
41 except KeyError:
42 print 'nothing!'
從上面的語句可以看到,dict用一種更優(yōu)雅的方式模擬了switch選擇,集合lambda函數(shù),還可以進一步實現(xiàn)更加復(fù)雜的邏輯分支語句。關(guān)于lambda函數(shù)的使用,我們到下一章再學習。
Python支持兩種循環(huán)語句——while循環(huán)和for循環(huán),不支持C#中的do-while循環(huán)。Python的while循環(huán)和C#基本一致,此處我們著重比較兩種語言中for循環(huán)的區(qū)別。
說的簡單一點,Python中的for語句相當于C#中的foreach語句,它常用于從集合對象(list、str、tuple等)中遍歷數(shù)據(jù)。例如:
1 for i in [1,2,3,4,5]:
2 print i
這與C#中的foreach語法基本是一樣的,下邊是C#中的對應(yīng)代碼:
1 IEnumerable<int> numbers = Enumerable.Range(0, 5);
2 foreach( int i in numbers)
3 Console.WriteLine(i);
如何實現(xiàn)類似C#中for(int i = 0; i < 10; i++)這種for循環(huán)呢?答案是使用range或xrange對象,見下邊的代碼:
1 # range(10)也可以用xrange(10)代替
2 for i in range(10):
3 print i
4 #等價于以下C#語句
5 #for(int i = 0; i<10;i++)
6 # Console.WriteLine(i);
內(nèi)建函數(shù)range([i,]j[,stride])建立一個整數(shù)列表,列表內(nèi)容為k(i <= k < j)。第一個參數(shù)i和第三個參數(shù)stride是可選的,默認值分別為 0 和 1。內(nèi)建函數(shù)xrange([i,]j[,stride])與 range 有相似之處,但xrange返回的是一個不可改變的XRangeType對象。這是一個迭代器,也就是只有用到那個數(shù)時才臨時通過計算提供值。當 j 值很大時,xrange能更有效地利用內(nèi)存。
Python中的while和for循環(huán)中支持break和continue語句。break語句用于立刻中止循環(huán),continue語句用于直接進入下一次循環(huán)(忽略當前循環(huán)的剩余語句)。break和continue語句在C#與Python中的用法是一致的,只用于語句所在的當前循環(huán)。如果需要退出一個多重循環(huán),應(yīng)該使用異常,因為Python中沒有提供goto語句。
最后,Python中的循環(huán)還支持else語句,它只在循環(huán)正常完成后運行(for和while循環(huán)),或者在循環(huán)條件不成立時立即運行(僅while循環(huán)),或者迭代序列為空時立即執(zhí)行(僅for循環(huán))。如果循環(huán)使用break語句退出的話,else語句將被忽略。下面的代碼用于說明else在循環(huán)中的應(yīng)用(引自《精要參考(第二版)》,有修改)。
1 # while-else
2 while i < 10:
3 i = i + 1
4 else:
5 print 'Done'
6 # for-else
7 for a in s:
8 if a == 'Foo':
9 break
10 else:
11 print 'Not found!'
Python和C#一樣支持異常處理,利用try/except/finally結(jié)構(gòu),可以很方便的捕獲異常,同時可以用raise語句手動拋出異常(上述四個異常處理的關(guān)鍵字分別對應(yīng)C#中的try/catch/finally/throw)。通過except,您可以將try標示的語句中出現(xiàn)的錯誤和異常捕獲,except可以接受參數(shù)作為要捕獲的異常,如果想要捕獲多個異常,可以使用元組(tuple)作為參數(shù)。沒有參數(shù)的except被認為是捕獲所有異常。而finally則用來在最后執(zhí)行一定要運行的代碼,例如資源回收。下面是一個簡單的例子,來說明Python中的異常處理方式:
1 try:
2 f = open('thefile.txt')
3 s = f.readline()
4 ...
5 except IOError, (errno, strerror):
6 print "I/O error(%s): %s" % (errno, strerror)
7 except ValueError:
8 print "Could not convert data to an integer."
9 except:
10 print "Unexpected error:", sys.exc_info()[0]
11 raise
12 finally:
13 f.close()
最后說明一點,Python的try也支持else語句。如果有一些代碼要在try沒有發(fā)生異常的情況下才執(zhí)行,就可以把它放到else中(這一點與finally不同,finally分支無論如何都會被執(zhí)行)。
關(guān)于異常處理我們就簡單介紹到這里,若需了解更多關(guān)于Python異常處理類、內(nèi)建異常類型、自定義異常等內(nèi)容,可以參考《Python精要參考(第二版)》。
列表內(nèi)涵(List Comprehensions,也譯作“列表推導(dǎo)式”)是Python最強有力的語法之一,常用于從集合對象中有選擇地獲取并計算元素,雖然多數(shù)情況下可以使用for、if等語句組合完成同樣的任務(wù),但列表內(nèi)涵書寫的代碼更簡潔(當然有時可能會不易讀)。
列表內(nèi)涵的一般形式如下,我們可以把[]內(nèi)的列表內(nèi)涵寫為一行,也可以寫為多行(一般來說多行更易讀)。
[表達式 for item1 in 序列1 ... for itemN in 序列N if 條件表達式]
上面的表達式分為三部分,最左邊是生成每個元素的表達式,然后是for 迭代過程,最右邊可以設(shè)定一個if 判斷作為過濾條件。
列表內(nèi)涵的一個著名例子是生成九九乘法表:
s = [(x, y, x*y) for x in range(1, 10) for y in range(1,10) if x>=y]
列表內(nèi)涵可能放在函數(shù)編程一章更合適,因為它可以統(tǒng)一實現(xiàn)map和filter等高階函數(shù)(下一章介紹)。不過我還是傾向于將它看為一種組合的流程控制語句,而且我個人感覺它與C#中的LINQ有點神似(當然LINQ更強大,可以處理數(shù)據(jù)庫和XML)。下面是兩個例子,一個用LINQ實現(xiàn),一個用Python的列表內(nèi)涵實現(xiàn)。
1 //C#中用LINQ找出10以內(nèi)的偶數(shù)
2 var s = from x in Enumerable.Range(0, 10) where x % 2 == 0 select x;
1 #Python中用列表內(nèi)涵模擬以上LINQ語句
2 s = [x for x in range(0, 10) if x % 2 == 0]
當然上邊的例子很簡單,實際上我們可以用列表內(nèi)涵完成更復(fù)雜的程序設(shè)計任務(wù),而且效率一般會比使用for、if等的組合語句高(因為中間省略了一些列表的生成和賦值過程)。Python 2.5 之后,列表內(nèi)涵進行了進一步的擴展,如果一個函數(shù)接受一個可迭代對象作為參數(shù),那么可以給它傳遞一個不帶中括號的列表內(nèi)涵,這樣就不需要一次生成整個列表,只要將可迭代對象傳遞給函數(shù)。
當然,列表內(nèi)涵也不能濫用,可能會嚴重影響代碼可讀性,一個典型的反面例子見我的另一篇文章《學習Python列表內(nèi)涵:一行代碼搞定雙倍超立方數(shù)計算》,這是初學者作品,謝絕效仿啊:)
先布置一個思考題:在C#語言中,如果需要在文本框中輸入1+2(或更復(fù)雜的數(shù)學表達式)后計算它的值,你會怎么做呢?
不怕大家笑,我在用C#解決這個問題的時候,是自己做了一個表達式解析器…………,雖然只能計算加減乘除的簡單組合,但也著實費了我好大力氣。后來才開始使用各種各樣的第三方Parse組件,msscript等?,F(xiàn)在我們有了Python,要完成這個任務(wù)可以說是非常簡單,簡單到你不敢相信:只要用內(nèi)置的eval()函數(shù),就可以計算并返回任意有效表達式的值。例如:
1 str = '1+2'
2 print eval(str)
你還可以試驗更復(fù)雜的表達式,是不是很Powerful的一項功能?
除了eval函數(shù)之外,Python還提供了exec語句將字符串str當成有效Python代碼來執(zhí)行,看下面的例子:
1 #exec.py
2 exec 'a=100'
3 print a
另外還有execfile函數(shù),它用來執(zhí)行一個外部的py文件。上一個例子存為exec.py后,運行下邊的代碼就知道是怎么回事了:
1 execfile(r'c:\exec.py')
最后提醒,默認的eval(),exec,execfile()所運行的代碼都位于當前的名字空間中,eval(), exec,和execfile()函數(shù)也可以接受一個或兩個可選字典參數(shù)作為代碼執(zhí)行的全局名字空間和局部名字空間,具體可以參考Python的手冊,我就不啰嗦了。
本章簡要比較了Python與C#在運算符、表達式、程序流程控制語句等方面的異同,要點如下:
(1) Python的算術(shù)運算符除了+、-、*、/、%之外,還有求冪(**)和取整除(//);
(2) Python支持復(fù)合賦值(x+=1),但不支持C#中的自增和自減運算符(如x++);
(3) Python用and、or、not代替了C#語言中的邏輯運算符&&、||和!,同時and、or所執(zhí)行的邏輯運算不返回布爾值,而是返回實際比較值之一;
(4) Python的選擇語句包括if、elif和else,記得后邊加冒號;Python中沒有switch,但可以用if-elif-else或dict完成相同的任務(wù)模擬;
(5) Python的循環(huán)語句有while、for,它們都支持else語句;注意Python的for相當于C#中的foreach語句,同時可以用for + range對象模擬C#中的常規(guī)for循環(huán);
(6) 兩種語言的異常處理語句基本一致,不同的是Python的異常處理也支持else。
本章比較簡單,沒什么資料好推薦給大家的,多數(shù)Python語法類書籍都會對運算符、表達式和流程控制語句進行詳細的講解,“百度一下,你就知道”!。不過我還是建議你試一下Python中的列表內(nèi)涵和動態(tài)表達式,兩個很pythonic的功能。
本章初稿完成于北京->石家莊的D4565次列車上。出差在外,旅途上寫寫blog消磨時間也是不錯滴:) 當然時間倉促,手頭資料也有限,如果有不恰當之處,請留言批評!
2010-3-5