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 '无论是否发生异常都执行'