Rendering of reStructured text is not possible, please install Docutils.
.. 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)]]