天天看點

python之條件-循環和其他語句

5.1.1 使用逗号輸出

如何使用print來列印表達式----不管是字元串還是其他類型進行自動轉換後的字元串。

>>> print 'age:' , 42

age: 42

>>>

>>> print 1,2,3

1 2 3

>>> 1,2,3

(1, 2, 3)

>>> print (1,2,3)

如果想要同時輸出文本和變量值,卻不希望使用字元串格式化的話,那這個特性就非常有用了

>>> name = 'Gumby'

>>> salutation = 'Mr'

>>> greeting = 'Hello'

>>> print greeting,salutation,name

Hello Mr Gumby

注意,如果greeting字元串不帶逗号,那麼結果中怎麼能得到逗号呢?像下面這樣做是不行的

print greeting,',',salutation,name

因為上面的語句會在逗号前加入空格。下面是一種解決方案:

print greeting + ',',salutation,name

這樣一來,問候語後面就隻會增加一個逗号.

如果在結尾處加上逗号,那麼接下來的語句會與前一條語句在同一行列印。例如:

print 'Hello,',

print 'world!'

5.1.2 把某件事作為另一件事導入

從子產品導入函數的時候,可以使用

import somemodule

或者

from somemodule import somefunction

from somemodule import somefunction,anotherfunction,yetanotherfunction

from somemodule import *

隻有确定自己想要從給定的子產品導入所有功能時,才應該使用最後一個版本。但是如果兩個子產品都有open函數,那又該怎麼辦?隻需使用第一種方式導入,然後像下面這樣使用函數:

module1.open(...)

module2.open(...)

但還有另外的選擇:可以再語句末尾增加一個as子句,在該子句後給出名字,或為整個子產品提供别名:

>>> import math as foobar

>>> foobar.sqrt(4)

2.0

也可以為函數提供别名

>>> from math import sqrt as foobar

>>> foobar(4)

對于open函數,可以像下面這樣使用

from module1 import open as open1

from modulel import open as open2

5.2 指派魔法

5.2.1 序列解包

指派語句的例子已經給過不少,其中包括對變量和資料結構成員的(比如清單中的位置和分片以及字典中的槽)指派。但指派的方法還不止這些。比如,多個指派操作可以同時進行:

>>> x,y,z = 1,2,3

>>> print x,y,z

用它交換兩個(或更多個)變量也是沒問題的:

>>> x,y = y,x

2 1 3

事實上,這裡所做的事情叫做序列解包或可選代解包------将多個值的序列解開,然後放到變量的序列中。更形象一點的表示出來就是:

>>> values = 1,2,3

>>> values

>>> x,y,z = values

>>> x

1

當函數或者方法傳回元組時,這個特性尤其有用。假設需要擷取字典中任意的鍵-值對,可以使用popitem方法,這個方法将鍵-值作為元組傳回。那麼這個元組就可以直接指派到兩個變量中:

>>> scoundrel = {'name':'Robin','girlfriend':'Marion'}

>>> key,value = scoundrel.popitem()

>>> key

'girlfriend'

>>> value

'Marion'

它允許函數傳回一個以上的值并且打包成元組,然後通過一個指派語句很容易進行通路。所解包的序列中的元素數量必須和放置在指派符号=左邊的變量數量完全一緻,否則python會在指派時引發異常:

>>> x,y,z = 1,2

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: need more than 2 values to unpack

>>> x,y,z = 1,2,3,4

ValueError: too many values to unpack

python3.0中有另外一個解包的特性:可以像在函數的參數清單中一樣使用星号運算符。

5.2.2 鍊式指派

鍊式指派是将同一個值賦給多個變量的捷徑.它看起來有些像上節中的并行指派,不過這裡隻處理一個值:

x = y = somefunction()

和下面語句的效果是一樣的

y = somefunction()

x = y

注意上面的語句和下面的語句不一定等價

x = somefunction()

5.2.3 增量指派

這裡沒有将指派表達式寫為x=x+1,而是将表達式運算符放置在指派運算符=的左邊,寫成x+=1.這種寫法叫做增量指派,對于* / %等标準運算符都适用

>>> x = 2

>>> x += 1

>>> x *= 2

6

對于其他資料類型也适用

>>> fnord = 'foo'

>>> fnord += 'bar'

>>> fnord *= 2

>>> fnord

'foobarfoobar'

5.3 語句塊:縮排的樂趣

語句塊并非一種語句,而是在掌握後面的兩節的内容之前應該了解的知識

語句塊是在條件為真時執行或執行多次的一組語句.在代碼前放置空格來縮進語句即可建立語句塊.

使用tab字元也可以縮進語句塊.python将一個tab字元解釋為到下一個tab字元位置的移動,而一個tab字元位置為8個空格,但是标準且推薦的方式是隻用空格,尤其是在每個縮進需要4個空格的時候.

塊中的每行都應該縮進同樣的量.下面的僞代碼展示了縮進的工作方式.

this is a line

this is another line:

this is another block

continuing the same block

the last line of this block

phew.there we escaped the inner block

很多語言使用特殊單詞或者字元來表示一個語句塊的開始,用另外的單詞或者字元表示語句塊的結束.在python中,冒号用來辨別語句塊的開始,塊中的每一個語句都是縮進的.當回退到和已經閉合的塊一樣的縮進量時,就表示目前塊已經結束了.

5.4 條件和條件語句

5.4.1 這就是布爾變量的作用

标準值False和None 所有類型的數字0 空序列以及空的字典都為假.其他的一切都被解釋為真,包括特殊值True.

也就是說python中的所有值都能被解釋為真值,初次接觸的時候可能會有些搞不明白,但是這點的确非常有用.标準的真值為True和False.在一些語言中,标準的真值為0和1.事實上,true和false隻不過是1和0的一種華麗的說法而已.

>>> true

NameError: name 'true' is not defined

>>> True

True

>>> False

False

>>> True == 1

>>> False == 0

>>> True + False + 43

44

那麼,如果某個邏輯表達式傳回1或0,那麼它實際的意思是傳回True或False

布爾值True和False屬于布爾類型,bool函數可以用來 ( 和list str以及tuple一樣 )轉換其他值

>>> bool('I think,therefore I am')

>>> bool(42)

>>> bool('')

>>> bool(0)

下面的值在作為布爾表達式的時候,會被解釋器看作假(false)

False None 0 “” () [] {} 

因為所有值都可以作為布爾值,是以幾乎不需要對它們進行顯示轉換.

盡管[]和""都是假值( 也就是說bool([])==bool("")==False ),它們本身卻并不相等 ( 也就是說[]!=""),對于其他不同類型的假值對象也是如此( 假如()!=False )

5.4.2 條件執行和if 語句

真值可以聯合使用,但還是讓我們先看看它們的作用。試着運作下面的腳本:

#!/usr/bin/env python

name = raw_input('What is your name?')

if name.endswith('Gumby'):

        print 'Hello,Mr.Gumby'

[root@pankuo ~]# ./python.py

What is your name?a

What is your name?Gumby

Hello,Mr.Gumby

這就是if語句,它可以實作條件執行。即如果條件(在if和冒号之間的表達式)判定為真,那麼後面的語句塊(本例中是單個print語句)機會被執行。如果條件為假,語句塊就不會被執行。

5.4.3 else子句

前一節的例子中,如果使用者輸入了以"Gumby" 作為結尾的名字,那麼name.endswith方法就會傳回真,使得if進入語句塊,列印出問候語。也可以用else子句增加一種選擇之是以叫做子句是因為它不是獨立的語句,而隻能作為if語句的一部分。

else:

        print 'Hello. stranger'

Hello. stranger

如果第一個語句塊沒有被執行,那麼就會轉入第二個語句塊,可以看到。

5.4.4 elif子句

如果需要檢查多個條件,就可以使用slif,它是"elif if" 的簡寫,也就是if和else子句的聯合使用-----也就是具有條件的else子句。

num = input('Enter a number:')

if num > 0 :

        print 'The number is positive'

elif num < 0:

        print 'The number is negative'

        print 'The number is zer0'

Enter a number:5

The number is positive

Enter a number:0

The number is zer0

可以使用int(raw_input(...))函數來代替input(...)。

5.4.5 嵌套代碼塊

name=raw_input('what is your name?')

if name.startswith('Mr.'):

print 'hello,Mr.Gumby'

elif name.startswith('Mrs.'):

print 'hello.Mrs.Gumby'

print 'hello.Gumby'

print 'hello.stranger'

如果名字以Gumby結尾的話,還要檢查名字的開頭

5.4.6 更複雜的條件

1.比較運算符

x==y

x〈y

x is y

x is not y

x in y   x是y容器(例如:序列)的成員

x not in y    x不是y容器(例如:序列)的成員

0<age<100

2.相等運算符

如果想知道兩個東西是否相等

〉〉〉“foo”==“foo”

〉〉〉“foo”==“bar”

3.is : 同一性運算符

>>>x=y=[1,2,3]

>>>z=[1,2,3]

>>>x==y

>>>x==z

>>>x is y

>>>x is z

== 檢測相等  is判斷兩者是否等同  is運算符是判定同一性而不是相等性

>>>x=[1,2,3]

>>>y=[2,4]

>>>x is not y

>>>del x[2]

>>>y[1]=1

>>>y.reverse()

盡量避免将is運算符用于比較類似數值和字元串這類不可變值。

4.in:成員資格運算符

if 's' in name:

print 'your name contains the letter "s".'

print 'your name does not contain the letter "s".'

5.字元串和序列比較

字元串可以按照字母順序排列進行比較。

>>> "alpha" < "beta"

>>> "a" < "b"

>>> "b" < "a"

>>> 

如果字元串内包括大寫字母,那麼結果就會有點亂。可以轉換大小寫在比較

>>> 'FnOrD'.lower() == 'Fnord'.lower()

其他的序列也可以同樣的方式進行比較,不過比較的不是字元而是元素的其他類型。

>>> [1,2]<[2,1]

如果一個序列中包括其他序列元素,比較規則也同樣适用于序列元素。

>>> [2,[1,4]]<[2,[1,5]]

6.布爾運算符

例如 如果需要編寫讀取數字并且判斷該數字是否位于1~10之間的程式,可以像下面這樣做:

number=input('Enter a number between 1 and 10: ')

if number <= 10:

        if number >=1:

                print 'Great!'

        else:

                print 'wrong!'

        print 'Wrong'

if number <= 10 and number >=1:

        print 'Great!'

        print 'Wrong!'

and運算符就是所謂的布爾運算符。它連接配接兩個布爾值,并且在兩者都為真時傳回真,否則傳回假。與它同樣的還有兩個運算符,or和not。使用這3個運算符就可以随意結合其值。

if ((cash > price) or customer_has_good_credit) and not out_of_stock:

give_goods()

表達式x and y需要兩個變量都為真時才為真,是以如果x為假,表達式就會立刻傳回false,而不管y的值。實際上,如果x為假,表達式會傳回x的值------否則它就傳回y的值。 這種行為被稱為短路邏輯或惰性求值:布爾運算符通常被稱為邏輯運算符,就像你看到的那樣第2個值時“被短路了”。這種行為對于or來說也同樣适用。在表達式x or y中,x為真時,它直接傳回x值,否則傳回y值。它主要是避免了無用地執行代碼,可以作為一種技巧使用,假設使用者應該輸入他/她的名字,但也可以選擇什麼都不輸入,這時可以使用預設值'<unknown>'.可以使用if語句,但是可以很簡潔的方式:

name=raw_input('Please enter your name: ') or '<unknown>'

換句話說,如果raw_input語句的傳回值為真,那麼它的值就會賦給name,否則将預設的'<unknown>'指派給name

a if b else c 如果b為真,傳回a,否則,傳回c

5.4.7 斷言

與其讓程式晚些時候崩潰,不如在錯誤條件出現時直接讓它崩潰 。一般說來,你可以要求某些條件必須為真。語句中使用的關鍵字assert。

>>> age=10

>>> assert 0 < age < 100

>>> age = -1

AssertionError

如果需要確定程式中的某個條件一定為真才能讓程式正常工作的話,assert語句就有用了,它可以在程式中置入檢查點。

條件後可以添加字元串,用來解釋斷言:

>>> assert 0 < age < 100,'The age must be realistic'

AssertionError: The age must be realistic

5.5 循環

5.5.1 while循環

x = 1

while x<=100:

print x

x += 1

 那麼python裡面應該如何呢?

name =  ''

while not name:

name=raw_input('Please enter your name:')

print 'Hello,%s!' % name    替換name

[root@pankuo ~]# python p.py 

Please enter your name:

Please enter your name:hah

Hello,hah!

運作這個程式看看,然後再程式要求輸入名字時按下Enter鍵。程式會再次要求輸入名字,因為name還是空字元串,其求值結果為false。

如果直接輸入一個空格作為名字又會如何?試試看。程式會接受這個名字,因為包括一個空格的字元串并不是空的,是以不會判定為假。小程式是以出現了瑕疵,修改起來也很簡單:隻需要把while not name改為while not name or name.isspace()即可,或者可以使用while not name.strip()。

5.5.2 for循環

while語句非常靈活。它可以用來在任何條件為真的情況下重複執行一個代碼塊。一般情況下這樣就夠用了,但是有些時候還得量體裁衣。比如要為一個集合(序列和其他可疊代對象)的每個元素都執行一個代碼塊。

可疊代對象是指可以按次序疊代的對象。

words = ['this','is','ex','parrot']

for word in words:

        print word

this

is

ex

parrot

因為疊代某範圍的數字是很常見的,是以有個内建的範圍函數供使用:

>>> range(0,10)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

range函數的工作方式類似于分片。它包含下限,但不包含上限

>>> range(11)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

下面的程式會列印1~100的數字

for number in range(1,101):

print number

它比之前的while循環更簡潔。

如果能使用for循環,就盡量不用while循環。

xrange函數的循環行為類似于range函數,差別在于range函數一次建立整個序列,而xrange一次隻建立一個數。當需要疊代一個巨大的序列時xrange會更高效,不過一般情況下不需要過多關注它。

5.5.3 循環周遊字典元素

一個簡單的for語句就能循環字典的所有鍵,就像處理序列一樣:

d = {'x':1,'y':2,'z':3}

for key in d:

        print key,'corresponds to',d[key]

y corresponds to 2

x corresponds to 1

z corresponds to 3

在python 2.2 之前,還隻能用keys等字典方法來擷取鍵。如果隻需要值,可以使用d.values替代d.keys。d.items 方法會将鍵-值對作為元組傳回,for循環的一大好處就是可以循環中使用序列解包:

for key,value in d.items():

print key,'corresponds to',value

字典元素的順序通常是沒有定義的,換句話說,疊代的時候,字典中的鍵和值都能保證被處理,但是處理順序不确定。如果順序很重要的話,可以将鍵值儲存在單獨的清單中,例如在疊代前進行排序。

5.5.4 一些疊代工具

1.并行疊代

程式可以同時疊代兩個序列。比如有下面兩個清單

names = ['anne','beth','george','damon']

ages = [12,45,32,102]

如果想要列印名字和對應的年齡,可以像下面這樣做:

for i in range(len(names)):

print names[i],'is',ages[i],'years old'

這裡i是循環索引的标準變量名。

而内建的zip函數可以用來進行并行疊代,可以把兩個序列“壓縮”在一起,然後傳回一個元組的清單:

>>> zip(names,ages)

[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]

現在我可以在循環中解包元組:

for name,age in zip(names,ages):

print name,'is ',age,'years old'

zip函數也可以作用于任意多的序列。關于它很重要的一點是zip可以應付不等長的序列:當最短的序列“用完”的時候就會停止:

>>> zip(range(5),xrange(6))

[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

>>> zip(range(5),xrange(2))

[(0, 0), (1, 1)]

>>> zip(range(5),xrange(5))

range會計算所有的數字,這要花費很長的時間。而使用xrange就沒這個問題,它隻計算前5個數字。

2.編号疊代

有些時候想要疊代序列中的對象,同時還要擷取目前對象的索引。例如,在一個字元串清單中替換所有包含‘xxx’的子字元串。實作的方法肯定有很多,假設你想象下面這樣做:

index = 0

for string in strings:

if ‘xxx’ in strings

strings[index] = ‘[censored]’

index +=1

方法有些笨,不過可以接受。另一種方法是使用内建的enumerate函數:

for index,string in enumerate(strings):

if 'xxx' in string:

strings[index] = 'censored'

這個函數可以提供索引的地方疊代索引-值對

3.翻轉和排序疊代 

讓我們看看另外兩個有用的函數:reversed和sorted:它們同清單的reverse和sort方法類似,但作用于任何序列或可疊代對象上,不是原地修改對象,而是傳回翻轉或排序後的版本:

>>> sorted([3,3,4,6,8])

[3, 3, 4, 6, 8]

>>> sorted ('hello,world!')

['!', ',', 'd', 'e', 'h', 'l', 'l', 'l', 'o', 'o', 'r', 'w']

>>> list(reversed('hello,world!'))

['!', 'd', 'l', 'r', 'o', 'w', ',', 'o', 'l', 'l', 'e', 'h']

>>> ''.join(reversed('hello,world!'))

'!dlrow,olleh'

注意,雖然sorted方法傳回清單,reversed方法卻傳回一個更加不可思議的可疊代對象。它們具體的含義不用過多關注,大可在for循環以及join方法中使用,而不會有任何問題。不過卻不能直接對它使用索引、分片以及調用list方法,如果希望進行上述處理,那麼可以使用list類型轉換傳回的對象。

5.5.5 跳出循環

一般來說,循環會一直執行到條件為假,或者到序列元素用完時。但是有些時候可能會提前中斷一個循環,進行新的疊代,或者僅僅就是想結束循環。

1.break

結束循環可以使用break語句。假設需要尋找100以内的最大平方數,那麼程式可以開始從100往下疊代到0.當找到一個平方數時就不需要繼續循環了,是以可以跳出循環:

from math import sqrt

for n in range(99,0,-1):

root = sqrt(n)

if root == int(root):

print n

break

如果執行這個程式的話,會列印出81,然後程式停止。注意,上面代碼中range函數增加了第3個參數---表示步長,步長表示每對相鄰數字之間的差别。将其設定為負值的話就會像例子中一樣反向疊代。它也可以用來跳過數字:

>>> range(0,10,2)

[0, 2, 4, 6, 8]

2.continue

continue語句比break語句用得要少得多,它會讓目前的疊代結束,“跳”到下一輪循環的開始。它最基本的意思是”跳過剩餘的循環體“,但是不結束循環。當循環體很大而且很複雜的時候,就會很有用,有些時候因為一些原因可能會跳過它----這個時候可以使用continue語句:

for x in seq:

if condition1: continue

if condition2: continue

if condition3: continue

do_something()

do_something_else()

do_anohten_thing()

etc()

很多時候,隻要使用if語句就可以了:

for x in seq:

if not (condition1 or condition2 or condition3):

do_another_thing()

盡管continue語句非常有用,它卻不是最本質的。應該習慣使用break語句,因為在while True語句中會經常用到它。

3.while True/break習語

python 中的while和for循環非常靈活,但一旦使用while語句就會遇到一個需要更多功能的問題。如果需要當使用者在提示符下輸入單詞時做一些事情,并且在使用者不輸入單詞後結束循環。

word='dummy'      啞值

while word:

word = raw_input('Please enter a word:')

#處理word:

print 'The word was' + word

Please enter a word:1

the word was 1

Please enter a word:2

the word was 2

Please enter a word:3

the word was 3

處理啞值

word = raw_input('Please enter a words:')

        print 'the word was ' + word

        word = raw_input('Please enter a words:')

減少重複代碼

while True:

        if not word: break

while True的部分實作了一個永遠不會自己停止的循環。但是在循環内部的if語句中加入條件可以的,在條件滿足時調用break語句。這樣以來就可以在循環内部任何地方而不是隻在開頭終止循環。if/break語句自然地将循環分為兩部分:第一部分負責初始化,第2部分則在循環條件為真的情況下使用第1部分内初始化好的資料。

盡管應該小心再代碼中頻繁使用break語句,但這個特殊的技術用得非常普遍,大多數python程式員都能了解你的意思。

5.5.6 循環中德else子句

當在循環内使用break語句時,通常是因為”找到了“某物或者因為某事”發生“了。跳出時做一些事情是很簡單的,但是有些時候想要在沒有跳出之前做些事情。那麼怎麼判斷呢?可以使用布爾變量,在循環前将其設定為False,跳出後設定為True。然後再使用if語句檢視循環是否跳出了:

broke_out = False

do_something(x)

if condition(x)

broke_out = True

do_something_else(x)

if not broke_out:

print "I didn't break out!"

更簡單的方式是在循環中增加一個else子句----它僅在沒有調用break時執行。讓我們用這個方法重寫剛才的例子:

for n in range(99,55,-1)

root=sqrt(n)

print "Didn't find it!"

81

100以内的最大平方數81

5.6 清單推導式------輕量級循環

清單推導式是利用其他清單建立新清單(類似于數學術語中的集合推導式)的一種方法。它的工作方式類似于for循環,也很簡單:

>>> [x*x for x in range(10)]

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

清單由range(10)中每個x的平方組成。如果隻想列印出那些能被3整除的平方數,那麼可以使用模除運算符-----y%3,當數字可以被3整除時傳回0(注意,x能被3整除時x的平方必然能被3整除)。這個語句可以通過增加一個if部分添加到清單推導式中:

>>> [x*x for x in range(10) if x % 3 == 0]

[0, 9, 36, 81]

也可以增加更多for語句的部分:

>>> [(x,y) for x in range(3) for y in range(3)]

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

result = []

for x in range(3):

for y in range(3)

result.append((x,y))

也可以和if子句聯合使用,像以前一樣:

>>> girls = ['alice','bernice','clarice']

>>> boys = ['chris','arnold','bob']

>>> [b+'+'+g for b in boys for g in girls if b[0] == g[0]]

['chris+clarice', 'arnold+alice', 'bob+bernice']

使用普通的圓括号而不是方括号不會得到‘元組推導式’

girls = ['alice','bernice','clarice']

boys = ['chris','arnold','bob']

letterGirls = {}

for girl in girls:

letterGirls.setdefault(girl[0],[]).append(girl)

print [b+'+'+g for b in boys for g in letterGirls[b[0]]]

更有方案

5.7 三人行

5.7.1 什麼都沒發生

>>> pass

可以在代碼中作為占位符使用。比如程式需要一個if語句,然後進行測試,但是缺少其中一個語句塊的代碼

if name == ‘Ralph Auldus Melish':

print 'Welcome!'

elif name == 'Enid':

#還沒完

elif name == 'Bill Gates'

print 'Access Denied'

代碼不會執行,因為不合法

但是如果這樣  就可以

pass

5.7.2 使用del删除

一般來說,python會删除那些不再使用的對象:

>>> scoundrel = {'age':42,'first name':'Robin','last name':'of Locksley'}

>>> robin = scoundrel

>>> scoundrel

{'last name': 'of Locksley', 'first name': 'Robin', 'age': 42}

>>> robin

>>> scoundrel = None

>>> robin = None

首先,robin和scoundrel都被綁定到同一個字典上,是以當設定scoundrel為None的時候,字典通過robin還是可用的。但是當我把robin也設定為None的時候,字典就’漂‘在記憶體裡面了,沒有任何名字綁定到它上面。沒有辦法擷取和使用它,是以python解釋器直接删除了那個字典(這種行為被稱為垃圾收集)。注意,也可以使用None之外的其他值。字典同樣會"消失不見"。

另外一個方法就是使用del。它不僅會移除一個對象的引用,也會移除那個名字本身

>>> x=1

>>> del x

NameError: name 'x' is not defined

x和y都指向同一個清單。但是删除x并不會影響y。原因就是删除的隻是名稱,而不是清單本身。事實上,在python中是沒有辦法删除值的(也不需要過多考慮删除值得問題,因為在某個值不再使用的時候,python解釋器會負責記憶體的回收)

>>> x = ["hello","world"]

>>> y=x

>>> y[1]="Python"

['hello', 'Python']

>>> y

5.7.3 使用exec和eval執行和求值字元串

有些時候可能會需要動态地創造python代碼,然後将其作為語句執行或作為表達式計算,這可能近似于”黑暗魔法“-------慎用

學到如何執行存儲在字元串中的python代碼。這樣做會有很嚴重的潛在安全漏洞。如果程式将使用者提供的一段内容的一部分字元串作為代碼執行,程式可能會失去對代碼執行的控制,這種情況在網絡應用程式----比如CGI腳本尤其危險。

1.exec

執行一個字元串的語句是exec:

>>> exec "print 'hellp,world!'"

hellp,world!

但是,使用簡單形式的exec語句絕不是好事。很多情況下可以給它提供命名空間----可以放置變量的地方。

>>> from math import sqrt

>>> exec "sqrt = 1"

>>> sqrt(4)

TypeError: 'int' object is not callable

exec語句最有用的地方在于可以動态地建立代碼字元串。如果字元串是從其他地方獲得的---很有可能是使用者---那麼幾乎不能确定其中到底包含什麼代碼。是以為了安全起見,可以增加一個字典,起到命名空間的作用

命名空間的概念,或成為作用域,是非常重要的知識。

可以通過增加in<scope>來實作,其中的<scope>就是起到放置代碼字元串命名空間作用的字典。

>>> scope={}

>>> exec 'sqrt = 1' in scope

>>> scope['sqrt']

可以看到潛在的破壞性代碼并不會覆寫sqrt函數,原來的函數能正常工作,而通過exec指派的變量sqrt隻在它的作用域内有效。

注意,如果需要将scope列印出來的話,會看到其中包含很多東西,因為内建的_builtins_字典自動包含所有的内建函數和值:

>>> len(scope)

>>> scope.keys()

['__builtins__', 'sqrt']

2.eval

eval用于’求值‘是類似于exec的内建函數。exec語句會執行一些列python語句,而eval會計算python表達式,并且傳回結果值,例如,可以使用下面的代碼建立一個python電腦:

>>> eval(raw_input("Enter an arithmetic expression:"))

Enter an arithmetic expression:6+1*3

9

表達式eval(raw_input(。。。))事實上等同于input(。。。)

跟exec一樣,eval也可以使用命名空間。盡管表達式幾乎不像語句那樣為變量重新指派。

盡管表達式一般不給變量重新指派,但它們的确可以。是以使用eval語句對付一些不可信任的代碼并不比exec更安全。

>>> scope['x']=2

>>> scope['y']=3

>>> eval('x*y',scope)

exec或者eval調用的作用域也能在另外一個上面使用:

>>> scope = {}

>>> exec 'x=2' in scope

>>> eval ('x*x',scope)

4

      本文轉自潘闊 51CTO部落格,原文連結:http://blog.51cto.com/pankuo/1661439,如需轉載請自行聯系原作者