Size: 6048
Comment:
|
Size: 6530
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 48: | Line 48: |
语法之前,大家实际都是用后面这种方法做的。 | 语法之前,大家一直都是用后面这种方法做的。 |
Line 50: | Line 50: |
是不是很简单?但其实也不是那么简单。要从复杂的来讲,它和所谓 AOP 之类的神秘概念都扯得上关系。python 就是这样,你总是能够以最简单的方式完成 |
是不是很简单?但其实又不是那么简单。要从复杂的来讲,它和所谓 AOP 之类的神秘概念都扯得上关系。但 python 就是这样,你总是能够以最简单的方式完成 |
Line 71: | Line 71: |
``func`` ,并在调用前后输出信息。 | ``func`` ,并在调用前后输出一些信息。 |
Line 73: | Line 73: |
随后它就可以被当做一个如假包换的 ``func`` 来用了。 | 于是这个 ``wrapper`` 就变成了一个被包装过的如假包换的 ``func`` 了! |
Line 75: | Line 75: |
现在的 log 是将信息输出到了标准输出,要是可以随意指定 log 输出的文件该多好啊, 也就是说,我们希望 log 变成这样: |
现在我们的 ``log`` 只是简单地将信息输出到了标准输出, 要是可以随意指定 ``log`` 输出到的文件该多好啊, 也就是说,我们希望 ``log`` 变成这样: |
Line 83: | Line 84: |
那么这个 log 应该怎么改呢?我们先来看一下上面这个代码的等价版本: | 那么这个 ``log`` 应该怎么实现呢?我们先来看一下上面这个代码的等价版本: |
Line 90: | Line 91: |
貌似这里括号有点多,但仔细一点就看得出来,log 需要接受一个文件对象做参数,并返回一个函数, 而返回的这个函数又接受一个函数作为参数并返回另一个函数。ok,我们还是看代码吧: |
貌似这里括号有点多,但仔细分析一下就看得出来, ``log`` 还是一个函数,它接受一个文件对象做参数, 并返回一个新函数,而这个新函数就是上面说过的装饰器(也就是绕口令:接受一个函数作为参数并返回另一个函数的函数)。 ok,我们还是看代码吧: |
Line 104: | Line 106: |
理清思路了吗? open('default.log') 返回一个文件对象,然后我们把它传给了 log , 并返回了一个 logger 函数,然后我们调用这个 logger 函数,把 test 函数传给了它, 它返回一个 wrapper 函数,而这个 wrapper 函数正是一个换过了的新 test 函数。 |
理清思路了吗? ``open('default.log')`` 返回一个文件对象,然后我们把它传给了 ``log`` , ``log`` 返回了这个 ``logger`` 函数,然后我们调用这个 ``logger`` 函数,把 ``test`` 函数传给了它, 它再返回一个 ``wrapper`` 函数,而这个 ``wrapper`` 函数正是一个包装过了的“新” ``test`` 函数。 |
Line 108: | Line 110: |
最后我们感觉这个 log 的功能还是有点弱,只能记录个 enter exit 和函数名,通常的 log 似乎都可以 记录下函数调用所用的参数和函数的返回值的: |
其实我们还是感觉这个 ``log`` 的功能有点弱,它只能记录下 enter、exit 和函数名, 作为一个有用的 ``log`` ,怎么说也应该能够记录下函数调用所用的参数和函数的返回值的吧,没问题: |
Line 125: | Line 127: |
从上面这个例子你应该看到不少熟悉的内容了吧 ;-) 让我们打开 python shell, 试试这个 log 的强大威力!先把上面的代码拷进 python shell(你应该可以从光盘中找到代码), |
从上面这个例子中你应该温习到了不少熟悉的内容了吧 ;-) 里面用到了字符串模板、字符串的 ``join`` 方法、 ``map`` 函数,Generator expressions、字典,不错,真是个好例子! 现在让我们打开 python shell,试试这个 log 的强大威力吧! 先把上面的代码拷进 python shell(你应该可以从光盘中找到代码), |
Line 171: | Line 176: |
是不是很神奇?哈哈。实际上,python 的生成器也可以有两种讲法, | 是不是很神奇?哈哈。啥?停不下来了?按一下 Ctrl+C 吧。 实际上,python 的生成器也可以有两种讲法, |
.. contents:: :status: 草稿 ;HuangYi; 10%; =================== Python函数 =================== 在介绍 python 函数之前不得不提的一个概念就是 python 的 callable 。 函数定义 ========== 神奇的星号 ============ lambda ========== 函数式编程 ============ 闭包(closure) =================== 装饰器(decorator) =================== :: @log def test(a, b): pass 其中 ``log`` 就是个别人写好的装饰器,作用就是在调用 ``test`` 的前后分别输出个 ``enter test`` 和 ``exit test`` ,使用符号 ``@`` 来应用这个装饰器。 用最容易理解的方式来说,装饰器其实很简单,我们给您看上面这段代码的另一种写法,就很清楚了: :: def test(a, b): pass test = log(test) 是的,上面两段代码完全等价!实际上在 python2.4 加上 ``@`` 语法之前,大家一直都是用后面这种方法做的。 是不是很简单?但其实又不是那么简单。要从复杂的来讲,它和所谓 AOP 之类的神秘概念都扯得上关系。但 python 就是这样,你总是能够以最简单的方式完成 一些看似复杂的工作 ;-) 那么这个 ``log`` 应该如何来写呢? 其实有经验的读者从后面这段代码中应该已经能够看出端倪。 ``log`` 无非就是接受一个函数作为参数同时返回一个新函数的函数,说起来像绕口令, 不如看代码: :: def log(func): def wrapper(*args, **kw): print 'enter', func.__name__ func(*args, **kw) print 'exit', func.__name__ wrapper.__name__ = func.__name__ wrapper.__globals__ = func.__globals__ #TODO: 好像还有其他的信息需要偷梁换柱,有待查资料。 return wrapper ``log`` 里面定义另一个叫 ``wrapper`` 的嵌套函数,它把所有接受到的参数简单地全部传给 ``func`` ,并在调用前后输出一些信息。 最后对 ``wrapper`` 的一些属性进行偷梁换柱之后,就将它返回了, 于是这个 ``wrapper`` 就变成了一个被包装过的如假包换的 ``func`` 了! 现在我们的 ``log`` 只是简单地将信息输出到了标准输出, 要是可以随意指定 ``log`` 输出到的文件该多好啊, 也就是说,我们希望 ``log`` 变成这样: :: @log(open('default.log', 'w')) # 这里 open 函数是以写方式打开一个文件,并返回这个文件对象 def test(a, b): pass 那么这个 ``log`` 应该怎么实现呢?我们先来看一下上面这个代码的等价版本: :: def test(a, b): pass test = log(open('default.log'))(test) 貌似这里括号有点多,但仔细分析一下就看得出来, ``log`` 还是一个函数,它接受一个文件对象做参数, 并返回一个新函数,而这个新函数就是上面说过的装饰器(也就是绕口令:接受一个函数作为参数并返回另一个函数的函数)。 ok,我们还是看代码吧: :: def log(fileobj): def logger(func): def wrapper(*args, **kw): print >> fileobj, 'enter', func.__name__ func(*args, **kw) print >> fileobj, 'exit', func.__name__ wrapper.__name__ = func.__name__ return wrapper return logger 理清思路了吗? ``open('default.log')`` 返回一个文件对象,然后我们把它传给了 ``log`` , ``log`` 返回了这个 ``logger`` 函数,然后我们调用这个 ``logger`` 函数,把 ``test`` 函数传给了它, 它再返回一个 ``wrapper`` 函数,而这个 ``wrapper`` 函数正是一个包装过了的“新” ``test`` 函数。 其实我们还是感觉这个 ``log`` 的功能有点弱,它只能记录下 enter、exit 和函数名, 作为一个有用的 ``log`` ,怎么说也应该能够记录下函数调用所用的参数和函数的返回值的吧,没问题: :: def log(fileobj): def logger(func): def wrapper(*args, **kw): print >> fileobj, 'calling function', func.__name__,\ 'with position arguments', ', '.join(map(str, args)),\ 'and keyword arguments',\ ', '.join('%s=%s'%key_value for key_value in kw.items()) result = func(*args, **kw) print >> fileobj, 'function', func.__name__, 'returns', result wrapper.__name__ = func.__name__ return wrapper return logger 从上面这个例子中你应该温习到了不少熟悉的内容了吧 ;-) 里面用到了字符串模板、字符串的 ``join`` 方法、 ``map`` 函数,Generator expressions、字典,不错,真是个好例子! 现在让我们打开 python shell,试试这个 log 的强大威力吧! 先把上面的代码拷进 python shell(你应该可以从光盘中找到代码), 然后: :: >>> import sys >>> @log(sys.stdout) # sys.stdout 就是标准输出,也就是 print 默认输出的地方 ... def plus(a, b): ... return int(a) + int(b) ... >>> plus(1, b=2) call function plus with position arguments 1 and keyword arguments b=2 function plus returns 3 >>> >>> logfile = open('func.log', 'w') >>> @log(logfile) ... def plus(a, b): ... return int(a) + int(b) ... >>> plus(1, b=2) >>> logfile.flush() >>> print open('func.log').read() call function plus with position arguments 1 and keyword arguments b=2 function plus returns 3 TODO:decorator tools 生成器(generator) ===================== :: >>> def number_generator(): ... i = 0 ... while True: ... yield i ... i += 1 ... >>> for item in number_generator(): ... print item ... 0 1 2 # 省略后面输出的无穷个数字 是不是很神奇?哈哈。啥?停不下来了?按一下 Ctrl+C 吧。 实际上,python 的生成器也可以有两种讲法, 从简单的来讲,它就是用来方便地实现迭代器,像上面用的那样。 从复杂的来讲呢,python 的生成器其实正是神秘的 continuation ! 实际上大部分时候我们都只需要把 yield 当成是快速实现迭代器的工具来用就行了。 TODO 小结 ========== 练习 =========== .. macro:: [[PageComment2(nosmiley=1, notify=1)]]