From: http://learnpythonthehardway.org/book/ex37.htmlhtml
1. with X as Y: passpython
1.1 yield express
2. exec編程
2.1 namespaceapp
3. lambdaide
3.1 map函數式編程
3.2 map and reduce函數
4. raise工具
KEYWORD | DESCRIPTION | EXAMPLE |
---|---|---|
and | Logical and. | True and False == False |
as (1) | Part of the with-as statement. | with X as Y: pass |
assert | Assert (ensure) that something is true. | assert False, "Error!" |
break | Stop this loop right now. | while True: break |
class | Define a class. | class Person(object) |
continue | Don't process more of the loop, do it again. | while True: continue |
def | Define a function. | def X(): pass |
del | Delete from dictionary. | del X[Y] |
elif | Else if condition. | if: X; elif: Y; else: J |
else | Else condition. | if: X; elif: Y; else: J |
except | If an exception happens, do this. | except ValueError, e: print e |
exec (2) | Run a string as Python. | exec 'print "hello"' |
finally | Exceptions or not, finally do this no matter what. | finally: pass |
for | Loop over a collection of things. | for X in Y: pass |
from | Importing specific parts of a module. | from x import Y |
global | Declare that you want a global variable. | global X |
if | If condition. | if: X; elif: Y; else: J |
import | Import a module into this one to use. | import os |
in | Part of for-loops. Also a test of X in Y. | for X in Y: pass also 1 in [1] == True |
is | Like == to test equality. | 1 is 1 == True |
lambda (3) | Create a short anonymous function. | s = lambda y: y ** y; s(3) |
not | Logical not. | not True == False |
or | Logical or. | True or False == True |
pass |
This block is empty. | def empty(): pass |
Print this string. | print 'this string' | |
raise (4) | Raise an exception when things go wrong. | raise ValueError("No") |
return | Exit the function with a return value. | def X(): return Y |
try | Try this block, and if exception, go to except. | try: pass |
while | While loop. | while X: pass |
with | With an expression as a variable do. | with X as Y: pass |
yield (1.1) | Pause here and return to caller. | def X(): yield Y; X().next() |
From: http://zhoutall.com/archives/325oop
(1) 常見寫法,但比較囉嗦
try: f = open('xxx') except: print 'fail to open' exit(-1) try: do something except: do something finally: f.close()
(2) 使用封裝如何?不用反覆寫finally,但致使:全部的函數都要被 controlled_execution( ) 下,太累贅。
def controlled_execution(callback): set things up try: callback(thing) finally: tear things down def my_function(thing): do something controlled_execution(my_function)
(3) 另外一個辦法是使用生成器,可是隻須要生成一次數據,咱們用for-in結構去調用他:
def controlled_execution(): //由於thing只有一個,因此yield語句只須要執行一次,從代碼可讀性也就是優雅的角度來講這簡直是糟糕透了 set things up try: yield thing //--> see "yield" finally: tear things down for thing in controlled_execution(): do something with thing
(4) with-as新方案
class controlled_execution: def __enter__(self): set things up return thing def __exit__(self, type, value, traceback): tear things down with controlled_execution() as thing: do something
當python執行這一句時,會調用__enter__函數,而後把該函數return的值傳給as後指定的變量。以後,python會執行下面do something的語句塊。最後不論在該語句塊出現了什麼異常,都會在離開時執行__exit__。
另外,__exit__除了用於tear things down,還能夠進行異常的監控和處理,注意後幾個參數。要跳過一個異常,只須要返回該函數True便可。
在python2.5及之後,file對象已經寫好了__enter__和__exit__函數,咱們能夠這樣測試:
>>> f = open("x.txt") >>> f <open file 'x.txt', mode 'r' at 0x00AE82F0> >>> f.__enter__() <open file 'x.txt', mode 'r' at 0x00AE82F0> >>> f.read(1) 'X' >>> f.__exit__(None, None, None) >>> f.read(1) Traceback (most recent call last): File "<stdin>", line 1, in <module>
以後,咱們若是要打開文件並保證最後關閉他,只須要這麼作:
with open("x.txt") as f: data = f.read() do something with data
若是有多個項,咱們能夠這麼寫:
with open("x.txt") as f1, open('xxx.txt') as f2: do something with f1,f2
上文說了__exit__函數能夠進行部分異常的處理,若是咱們不在這個函數中處理異常,他會正常拋出,這時候咱們能夠這樣寫(python 2.7及以上版本,以前的版本參考使用contextlib.nested這個庫函數):
try: with open( "a.txt" ) as f : do something except xxxError: do something about exception
總之,with-as表達式極大的簡化了每次寫finally的工做,這對保持代碼的優雅性是有極大幫助的。
From: http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
def fab(max): n, a, b = 0, 0, 1 while n < max: print b // 不太好! 其實改成yield就行了。 a, b = b, a + b n = n + 1
執行 fab(5),咱們能夠獲得以下輸出:
>>> fab(5) 1 1 2 3 5
結果沒有問題,但有經驗的開發者會指出,直接在 fab 函數中用 print 打印數字會致使該函數可複用性較差,由於 fab 函數返回 None,其餘函數沒法得到該函數生成的數列。
要提升 fab 函數的可複用性,最好不要直接打印出數列,而是返回一個 List。
如下是 fab 函數改寫後的第二個版本:
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L // 暫用內存太大!
可使用以下方式打印出 fab 函數返回的 List:
>>> for n in fab(5): ... print n ... 1 1 2 3 5
改寫後的 fab 函數經過返回 List 能知足複用性的要求,可是更有經驗的開發者會指出,該函數在運行中佔用的內存會隨着參數 max 的增大而增大。
若是要控制內存佔用,最好不要用 List來保存中間結果,而是經過 iterable 對象來迭代。例如,在 Python2.x 中,代碼:
for i in range(1000): pass //會致使生成一個 1000 個元素的 List
for i in xrange(1000): pass // iterable對象是解決的辦法!
在每次迭代中返回下一個數值,內存空間佔用很小。由於 xrange 不返回 List,而是返回一個 iterable 對象。
利用 iterable 咱們能夠把 fab 函數改寫爲一個支持 iterable 的 class,如下是第三個版本的 Fab:
class Fab(object): def __init__(self, max): self.max = max self.n, self.a, self.b = 0, 0, 1 def __iter__(self): return self def next(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r raise StopIteration()
Fab 類經過 next() 不斷返回數列的下一個數,內存佔用始終爲常數:
>>> for n in Fab(5): ... print n ... 1 1 2 3 5
然而,使用 class 改寫的這個版本,代碼遠遠沒有初版的 fab 函數來得簡潔。
若是咱們想要保持初版 fab 函數的簡潔性,同時又要得到 iterable 的效果,yield 就派上用場了:
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # print b a, b = b, a + b n = n + 1
第四個版本的 fab 和初版相比,僅僅把 print b 改成了 yield b,就在保持簡潔性的同時得到了 iterable 的效果。
調用第四版的 fab 和第二版的 fab 徹底一致:
>>> for n in fab(5): // for執行一次函數,其實只是調用了函數內的一次運算;再調一次就再繼續算一次! ... print n ... 1 1 2 3 5
也能夠手動調用 fab(5) 的 next() 方法(由於 fab(5) 是一個 generator 對象,該對象具備 next() 方法),這樣咱們就能夠更清楚地看到 fab 的執行流程:
>>> f = fab(5) // fab(5) 是iterable function,這裏就是指的fab的實例 >>> f.next() // 與清單4 中的next比較下,實際上是相同的思想 1 >>> f.next() 1 >>> f.next() 2 >>> f.next() 3 >>> f.next() 5 >>> f.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
當函數執行結束時,generator 自動拋出 StopIteration 異常,表示迭代完成。在 for 循環裏,無需處理 StopIteration 異常,循環會正常結束。
From: http://blog.sina.com.cn/s/blog_76e94d210100w1bl.html
exec
語句用來執行儲存在字符串或文件中的Python語句。
例如,咱們能夠在運行時生成一個包含Python代碼的字符串,而後使用exec
語句執行這些語句。
下面是一個簡單的例子。
>>> exec 'print "Hello World"' Hello World
eval
語句用來計算存儲在字符串中的有效Python表達式。下面是一個簡單的例子。
>>> eval_r('2*3') 6
eval_r(str [ globals [ locals ]])函數將字符串str當成有效python表達式來求值,並返回計算結果。
一樣地, exec語句將字符串str當成有效Python代碼來執。.提供給exec的代碼的名稱空間和exec語句的名稱空間相同。
最後,execfile(filename [, globals [, locals ]]) 函數能夠用來執行一個文件。
>>> eval_r('3+4') 7 >>> exec 'a=100' >>> a 100 >>> execfile(r'd:\code\ex\test.py') hello world! >>>
默認的,eval_r(), exec, execfile() 所運行的代碼都位於當前的名字空間中.
eval_r(), exec 和 execfile()函數 也能夠接受一個或兩個可選字典參數做爲代碼執行的全局名字空間和局部名字空間。
From: http://blog.cipherc.com/2015/04/25/python_namespace_and_scope/#assignment-rule
Namespace(只)是 從名字到對象的一個映射 (a mapping from name to objects) 。
大部分namespace都是按Python中的字典來實現的。
有一些常見的namespace:built-in中的集合( abs()
函數等)、一個模塊中的全局變量等。
從某種意義上來講,一個對象(object)的全部屬性(attribute)也構成了一個namespace.
在程序執行期間,可能(實際上是確定)會有多個名空間同時存在。不一樣namespace的建立/銷燬時間也不一樣。此外,兩個不一樣namespace中的兩個相同名字的變量之間沒有任何聯繫。
Scope是Python程序的一塊文本區域(textual region)。
在該文本區域中,對namespace是能夠直接訪問,而不須要經過屬性來訪問。
Scope是定義程序該如何搜索確切地「名字-對象」的名空間的層級關係。(The 「scope」 in Python defines the 「hirerchy level」 in which we search namespaces for certain 「name-to-object」 mappings.)
直接訪問:對一個變量名的引用會在全部namespace中查找該變量,而不是經過屬性訪問。
屬性訪問:全部名字後加.的都認爲是屬性訪問。如 module_name.func_name,須要指定 func_name 的名空間,屬於屬性訪問;而 abs(-1),abs 屬於直接訪問。
在Python中,scope是由namespace按特定的層級結構組合起來的。
scope必定是namespace,但namespace不必定是scope。 // 感受namespace範圍更大?
在一個Python程序運行中,至少有4個scopes是存在的。
直接訪問一個變量可能在這四個namespace中逐一搜索。
包含局部變量。
好比一個函數/方法內部。
包含了非局部(non-local)也非全局(non-global)的變量。
好比兩個嵌套函數,內層函數可能搜索外層函數的namespace,但該namespace對內層函數而言既非局部也非全局。
當前腳本的最外層。
好比當前模塊的全局變量。
Python __builtin__
模塊。
包含了內建的變量/關鍵字等。
* 那麼,這麼多的做用域,Python是按什麼順序搜索對應做用域的呢?
* 著名的」LEGB-rule」,即scope的搜索順序:
Local -> Enclosing -> Global -> Built-in
當有一個變量在 local 域中找不到時,Python會找上一層的做用域,即 enclosing 域(該域不必定存在)。
enclosing 域還找不到的時候,再往上一層,搜索模塊內的 global 域。最後,會在 built-in 域中搜索。
對於最終沒有搜索到時,Python會拋出一個 NameError
異常。
做用域能夠嵌套。好比模塊導入時。
這也是爲何不推薦使用 from a_module import * 的緣由,導入的變量可能被當前模塊覆蓋。
兩條很重要的規則:
也就是說在做用域內有沒有發生賦值是不同的。
可是,在這點上,Python 2和Python 3又有不一樣, Python access non-local variable:
Python’s scoping rules indicate that a function defines a new scope level, and a name is bound to a value in only one scope level – it is statically scoped. … In Python 2.x, it is not possible to modify a non-local variable; 1) you have either read-only access to a global or non-local variable, 2) or read-write access to a global variable by using the global statement, 3) or read-write access to a local variable (by default). In Python 3.x, the nonlocal statement has been introduced with a similar effect to global, but for an intermediate scope.
CipherChen@CIPHERC ~/Development/Workspace/test_python $ python2 Python 2.7.9 (default, Jan 25 2015, 13:42:57) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> for i in range(10): print i ... 0 1 2 3 4 5 6 7 8 9 >>> print i 9 <---- 大坑 >>>
for
後面跟着的變量(target list)在循環結束後是不會被刪除的,
但若是 for
循環的序列爲空,這些變量是徹底不會被賦值的。
這在Python中是個大坑啊。
避免這個坑的解決辦法就是規範命名規範。
好比用於循環的變量儘可能使用單字符。在任何有疑議的狀況能夠直接將索引值初始化。
很不幸,Python 3中這點沒有改變。
class A(object): a = 3 b = list(a + i for i in range(10))
cipher@Rachel ~/Development/Workspace/test_Python $ python a.py
Traceback (most recent call last): File "a.py", line 3, in <module> class A(object): File "a.py", line 5, in A b = list(a + i for i in range(10)) File "a.py", line 5, in <genexpr> b = list(a + i for i in range(10)) NameError: global name 'a' is not defined
class沒有做用域(scope),但有一個局部的名空間(namespace),它並不構成一個做用域。
---- 這意味着在類定義中的表達式能夠訪問該名空間。
但在類體(class body)中, 對 b
的賦值表達式中,該表達式引入了一個新的做用域,該做用域並不能訪問類的名空間。
就像剛剛說的,函數會引入一個新的做用域(以前定義的a失效了),因此報錯!
class C(object): a = 2 def foo(self): return a # NameError: name 'a' is not defined, use return self.__class__.a
未引入新的做用域的例子,因此能夠執行 print a:
Python 2.7.9 (default, Jan 25 2015, 13:42:57) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> [a for a in range(3)] [0, 1, 2] >>> print a //未引入新的做用域 就能夠直接讀出持續的值 2
要解決這個問題(哪一個問題?),有幾種解決辦法:
1. 用生成器表達式
b = [a + i for i in range(10)]
2. 用函數/lambda引入新的做用域
b = (lambda a: ((a + i for i in range(10))))(a)
Can access class attributes | Python 2 | Python 3 |
list comp. iterable | Y | Y |
list comp. expression | Y | N |
gen expr. iterable | Y | Y |
gen expr. expression | N | N |
dict comp. iterable | Y | Y |
dict comp. expression | N | N |
map( lambda x: x*x, [y for y in range(10)] )
def sq(x): return x * x map(sq, [y for y in range(10)])
a = [1, 2, 3]
f = lambda x : x + 1
map(f, a)
map( lambda x : x + 1, [1, 2, 3] )
lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
lambda表達式是起到一個函數速寫的做用。容許在代碼內嵌入一個函數的定義。
以下例子:定義了一個lambda表達式,求三個數的和。
一種表達式,能夠帶參數的表達式,參數即給最外層的lambda的賦值,而後return表達式的計算結果。
看例子:
這裏定義了一個action函數,返回了一個lambda表達式。其中lambda表達式獲取到了上層def做用域的變量名x的值。
a是action函數的返回值,a(22),便是調用了action返回的lambda表達式。
map(function, sequence[, sequence, ...]) -> list
map(lambda x: x ** 2, [1, 2, 3, 4, 5])
返回結果爲:
map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
map返回的list中第一個元素爲,參數序列1的第一個元素加參數序列2中的第一個元素(1 + 2),
list中的第二個元素爲,參數序列1中的第二個元素加參數序列2中的第二個元素(3 + 4),
依次類推,最後的返回結果爲:
[3, 7, 11, 15, 19]
map(None, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
返回結果爲:
提起map和reduce想必你們並不陌生,Google公司2003年提出了一個名爲MapReduce的編程模型[1],用於處理大規模海量數據,並在以後普遍的應用於Google的各項應用中,2006年Apache的Hadoop項目[2]正式將MapReduce歸入到項目中。
好吧,閒話少說,今天要介紹的是Python函數式編程中的另外兩個內建函數 map()
和reduce()
,而不是Google的MapReduce。
格式:map( func, seq1[, seq2...] )
Python函數式編程中的map()
函數是將func做用於seq中的每個元素,並用一個列表給出返回值。若是func爲None,做用同zip()
。 // 同3.1
當seq只有一個時,將func函數做用於這個seq的每一個元素上,獲得一個新的seq。
下圖說明了只有一個seq的時候map()函數是如何工做的(本文圖片來源:《Core Python Programming (2nd edition)》)。
能夠看出,seq中的每一個元素都通過了func函數的做用,獲得了func(seq[n])組成的列表。
# 使用map print map( lambda x: x%3, range(6) ) # [0, 1, 2, 3, 4, 5] --> [0, 1, 2, 0, 1, 2] #使用列表解析 print [x%3 for x in range(6)] # [0, 1, 2, 0, 1, 2] 多於一個range,就沒辦法搞了
這裏又和上次的filter()
同樣,使用了列表解析的方法代替map執行。那麼,何時是列表解析沒法代替map的呢?
原來,當seq多於一個時,map能夠並行地對每一個seq執行以下圖所示的過程:
也就是說每一個seq的同一位置的元素在執行過一個多元的func函數以後,獲得一個返回值,這些返回值放在一個結果列表中。
下面的例子是求兩個列表對應元素的積,能夠想象,這是一種可能會常常出現的情況,而若是不是用map的話,就要使用一個for循環,依次對每一個位置執行該函數。
print map( lambda x, y: x*y, [1, 2, 3], [4, 5, 6] ) # [4, 10, 18]
也能夠是一個元組。下面的代碼不止實現了乘法,也實現了加法,並把積與和放在一個元組中。
還有就是上面說的func是None的狀況,它的目的是將多個列表相同位置的元素歸併到一個元組,在如今已經有了專用的函數zip()
了。
print map( None, [1, 2, 3], [4, 5, 6] ) # [(1, 4), (2, 5), (3, 6)] print zip( [1, 2, 3], [4, 5, 6] ) # [(1, 4), (2, 5), (3, 6)]
須要注意的是,不一樣長度的多個seq是沒法執行map函數的,會出現類型錯誤。
格式:reduce( func, seq[, init] )
reduce函數即爲化簡,它是這樣一個過程:
"每次迭代,將上一次的迭代結果(第一次時爲init的元素,如沒有init則爲seq的第一個元素)與下一個元素一同執行一個二元的func函數。"
在reduce函數中,init是可選的,若是使用,則做爲第一次迭代的第一個元素使用。
簡單來講,能夠用這樣一個形象化的式子來講明:
reduce( func, [1, 2, 3] ) = func( func(1, 2), 3)
下面是reduce函數的工做過程圖:
舉個例子來講,階乘是一個常見的數學方法,Python中並無給出一個階乘的內建函數,咱們可使用reduce實現一個階乘的代碼。
n = 5 print reduce(lambda x,y: x*y, range(1, n + 1)) # 120
那麼,若是咱們但願獲得2倍階乘的值呢?這就能夠用到init這個可選參數了。
m = 2 n = 5 print reduce( lambda x,y: x*y, range( 1, n + 1 ), m ) # 240
From: http://www.cnblogs.com/IPrograming/p/Python_error_handler.html
Python用異常對象(exception object)表示異常狀況,遇到錯誤後,會引起異常。
若是異常對象並未被處理或捕捉,程序就會用所謂的回溯(Traceback,一種錯誤信息)終止執行。
Python中的raise 關鍵字用於引起一個異常,基本上和C#和Java中的throw關鍵字相同,以下所示:
1 # -- coding: utf-8 -- 2 3 def ThorwErr(): 4 raise Exception("拋出一個異常") # 關鍵字後面是拋出是一個通用的異常類型(Exception), 5 6 # Exception: 拋出一個異常 7 ThorwErr()raise
通常來講拋出的異常越詳細越好,Python在exceptions
模塊內建了不少的異常類型,經過使用dir
函數來查看exceptions
中的異常類型,以下:
import exceptions # ['ArithmeticError', 'AssertionError'.....] print dir(exceptions)
傳遞異常
捕捉到了異常,可是又想從新引起它(傳遞異常),可使用不帶參數的raise
語句便可:
1 # -- coding: utf-8 --
2 class MuffledCalculator:
3 muffled = False
4 def calc(self,expr):
5 try:
6 return eval(expr)
7 except ZeroDivisionError:
8 if self.muffled:
9 print 'Division by zero is illegal'
10 else:
11 raise
Python中也能夠自定義本身的特殊類型的異常,只須要要從Exception類繼承(直接或間接)便可:
class SomeCustomException(Exception): pass
和C#中的try/catch
相似,Python中使用try/except
關鍵字來捕捉異常,以下:
# -- coding: utf-8 -- try: print 2/0 except ZeroDivisionError: print '除數不能爲0'
在一個except語句只捕捉其後聲明的異常類型,若是可能會拋出的是其餘類型的異常就須要再增長一個except語句了,或者也能夠指定一個更通用的異常類型好比:Exception
,以下:
# -- coding: utf-8 -- try: print 2/'0' except ZeroDivisionError: print '除數不能爲0' except Exception: print '其餘類型異常'
爲了捕獲多個異常,除了聲明多個except語句以外,還能夠在一個except語句以後將多個異常做爲元組列出來便可:
# -- coding: utf-8 -- try: print 2/'0' except (ZeroDivisionError,Exception): print '發生了一個異常'
每一個異常都會有一些異常信息,通常狀況下咱們應該把這些異常信息記錄下來:
# -- coding: utf-8 -- try: print 2/'0' except (ZeroDivisionError,Exception) as e: # unsupported operand type(s) for /: 'int' and 'str' print e
finally
子句和try
子句聯合使用可是和except語句不一樣,finally
無論try
子句內部是否有異常發生,都會執行finally
子句內的代碼。全部通常狀況下,finally
本身經常用於關閉文件或者在Socket中。
# -- coding: utf-8 -- try: print 2/'0' except (ZeroDivisionError,Exception): print '發生了一個異常' finally: print '無論是否發生異常都執行'