接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数。
如下面的代码,*rest包含了2,3,4这3个参数。且可以迭代访问。在这个例子中,rest其实就是其他位置参数组成的一个元组
def avg(first,*rest):
    for i in rest:
        print i
    average=(first+sum(rest))/(1+len(rest))
    print average
avg(1,2,3,4)
如果我们需要传递带关键字的参数:可以用下面的方法。当采用**attr的时候,attr其实是一个包含所有被传入进来的关键字参数的字典
def avg(first,**attr):
    for item in attr.items():
        print item,attr[item[0]]
avg(1,size='large',quantity=0)
得到的结果如下:
E:\python2.7.11\python.exe E:/py_prj/python_cookbook.py
('quantity', 0) 0
('size', 'large') large
 
如果想强制让某些参数用关键字参数传递,可以将强制关键字参数放到*参数后面或者单个的*后面
 
Lambda:
在C语言中,对于一些短小的实现。可以不用函数而是写一个宏的方式来实现。
比如#define M(y) y*y+3*y   那么M(5)最终的结果就是5*5+3*5=40
在python中也有类似的实现,这就是lambada。代码如下,这就是实现了一个加法的lambda
add=lambda x,y:x+y
print add(3,4)
再来看下面的这个例子:
我们想对name中的名字按照姓来进行排序。在sorted中指定key值也就是排序的依据。在这里用lambda name:name.split()[-1].lower()的方式将每个名字的姓提取出来,然后在赋值给key
name=['David Beazley','Brains Jones','Raymond Hettinger','Ned Batchelder']
ret=sorted(name,key=lambda name:name.split()[-1].lower())
print ret
来看一个比较有意思的例子:
x=10
a=lambda y:y+x
x=20
b=lambda y:y+x
print a(10)
print b(10)
上面的代码a(10)和b(10)的值是多少,20和30?直观上是对的,但是实际上结果却是30和30. 为什么会这样呢。我们来单步运行下:
首先运行了a=lambda y:y+x 后,对应的x=10
在运行了b=lambda y:y+x后,对应的x=20

此时执行到print a(10)。这个时候跳转到a=lambda y:y+x 此时的x=20,因此a(10)就等于20+10=30

从这上面的例子可以看出,尽管我们在lambda之前定义了x的值。但是最终的值并不是定义的时候就绑定,而是在实际lambda运行时才开始绑定。而在lambda实际运行也就是print a(10)的时候,x的值已经被赋值成了20.
如果想让参数在lambda定义的时候就固定,需要在定义的时候就设置参数的值
a=lambda y,x=x:y+x
在lambda中设置x=x,这样就将x的值绑定成了10这个值。
这个在列表推导的时候特别容易出错。如下面的代码。我们期望得到的是0,1,2,3,4
fun=[lambda x:x+n for n in range(5)]
for f in fun:
    print f(0)
但是最终的结果却是4,4,4,4,4. 原因和之前的一样,n在被调用的时候被为4。
改成如下代码,n的值就可以每次都被绑定了。
fun=[lambda x,n=n:x+n for n in range(5)]
 
partial使用:
如果函数的参数值太多,partial可以固定一个或多个参数的值,在调用的时候可以减少调用的参数个数。如下面的代码。固定d=3,在调用的时候只需要设置a,b,c的值
def spam(a,b,c,d):
    print a,b,c,d
s1=partial(spam,d=3)
s1(0,1,2)
 
来看一个实际的例子,假设你有一个点的列表来表示(x,y)坐标元组。你可以使用下面的函数来实现
points=[(1,2),(3,4),(5,6),(7,6)]
 
def distance(p1,p2):
    x1,y1=p1
    x2,y2=p2
    return math.hypot(x2-x1,y2-y1)
但是如果我们想计算到某个基点的距离,并基于这个距离来进行排序。该如何操作呢。之前我们讲了lambda的用法。我们可以用lambda来实现,代码如下
points=[(1,2),(3,4),(5,6),(7,6)]
pt=(4,3)
ret=sorted(points,key=lambda points: math.hypot(pt[0]-points[0],pt[1]-points[1]))
print ret
用lambda实现稍显冗余。Partial可以精简下代码。在这里我们将distance的第二个参数p2固定为pt。这样在调用的时候其实就不
ret=sorted(points,key=partial(distance,pt))
print ret
这样在调用的时候其实就是下面的样式:
distance((1,2),(4,3))
distance((3,4),(4,3))
distance((5,6),(4,3))
distance((7,6),(4,3))
 
 
 
回调函数:
回调函数在C语言中经常使用,简单来说就是将回调函数的指针地址作为参数传递一个函数,而那个函数在需要用到的时候利用传递的地址回调函数。这时就可以利用这个机会在回调函数中处理或者完成操作。
比如下面的C语言代码。printWelcome的地址传递给(*print)(int).在callback中就可以调用

void printWelcome(int len)

{

printf("welcome -- %d/n", len);

}

 

void callback(int times, void (* print_info)(int))

{

int i;

for (i = 0; i < times; ++i)

{

Print_info(i);

}

void main(void)

{

callback(10, printWelcome);

}

我们通俗点来说,回调函数就好比你去商店买了东西,但是没货,这个时候你留了电话号码给店员,等到有货的时候店员打电话给你让你取取货。你的电话号码就相当于回调函数。
来看下python中的回调函数如何用:
def apply_async(func,args,callback):
    result=func(*args)
    callback(result) def print_result(result):
    print result
def add(x,y):
    return x+y
apply_async(add,(2,3),callback=print_result)
在apply_async中设置callback为print_result.当add加法运行完以后,则可以调用print_result来打印。
有人会问,这和写中间函数有什么区别呢:代码改成如下不也是一样,在appy_async中调用print_result不是一样的么
def apply_async(func,args):
    result=func(*args)
    print_result(result) def print_result(result):
    print result def add(x,y):
    return x+y if __name__=='__main__':
    apply_async(add,(2,3))
 
确实这样写也是一样的效果,那么回调函数有什么好处呢?如果更新下我们的需求,在apply_async中我们还想打印出2个参数相减的结果。代码可以改成如下
def apply_async(func,func1,args):
    result=func(*args)
    print_result(result)
    result1=func1(*args)
    print_result(result1) def print_result(result):
    print result def delete(x,y):
    return x-y def add(x,y):
    return x+y if __name__=='__main__':
    apply_async(add,delete,(2,3))
 
但是如果需求继续增加,我们还想打印乘法,除法,幂运算等各种运算结果呢。这个时候在apply_async中不是得传递各种函数参数。参数越写愈多,也越来越不好看。这个时候回调函数的优势就体现出来了。
def apply_async(func,args,callback):
    result=func(*args)
    callback(result) def print_result(result):
    print result def delete(x,y):
    return x-y def add(x,y):
    return x+y if __name__=='__main__':
    apply_async(add,(2,3),callback=print_result)
    apply_async(delete,(2,3),callback=print_result)
 
看到没,我只需要给apply_async的第一个参数传递不同的处理函数,我就可以得到不同的结果。而回调函数都是固定的。这就相当于将公共部分用回调函数来处理。而apply_async通过参数的传递得到了不同的结果。
在上面的回调函数中,回调函数只能处理传入的参数,无法访问其他变量。
为了让回调函数访问外部信息,有两种方法:1 使用一个绑定方法来代替一个简单函数
 
class ResultHandler:
    def __init__(self):
        self.sequence=0
    def handler(self,result):
        self.sequence+=1
        print self.sequence,result
r=ResultHandler()
apply_async(add,(2,3),r.handler)
apply_async(add,(4,4),r.handler)
 
在这里首先创建类ResultHandler的实例,然后用handler来作为回调函数,这个时候就可以同步访问sequence这个变量
 
第二种方法就是使用闭包。
def make_handler():
    sequence=0
    def handler(result):
        nonlocal sequence
        sequence+=1
        print sequence,result
    return handler
hanlder=make_handler()
apply_async(add,(2,3),callback=hanlder)
 
注意nonlocal是在Python3.0才使用的。在2.x是没有这个关键字的。因此在2.x中要么使用全局变量,要么使用列表或者字典。如果使用变量,会报错。因此系统不知道这个变量是在哪引用的。
def make_handler():
    sequence=[1]
    def handler(result):
        sequence[0]=2
        print sequence,result
    return handler
 
 
内联回调函数:
首先来看下functools中wrap修饰器的用法。首先来看下
def decorator_try(func):
    def wrapper(*args,**kwargs):
        print 'call decorator_try'
        return
func(*args,**kwargs)
    return wrapper @decorator_try
def example():
    print 'call example' if __name__=='__main__':
    example()
    print example.__name__
结果如下:
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter5.py
call decorator_try
call example
wrapper
 
在这里example.__name__的结果是wrapper,而非example.也就是被修饰的函数的属性发生了改变。因为装饰器可以等效写成 example=decorator_try(example).而decorator_try的返回值是wrapper.因此example的属性也跟着变成了wrapper。要消除这样的影响,就要用到wraps
用wraps修饰一下wrapper后,得到的结果就是example
def decorator_try(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        print 'call decorator_try'
        return
func(*args,**kwargs)
    return wrapper
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter5.py
call decorator_try
call example
example

 

python cookbook第三版学习笔记九:函数的更多相关文章

  1. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  2. python cookbook第三版学习笔记十九:未包装的函数添加参数

    比如有下面如下的代码,每个函数都需要判断debug的是否为True,而默认的debug为False def a(x,debug=False): if debug: print('calling a') ...

  3. python cookbook第三版学习笔记六:迭代器与生成器

    假如我们有一个列表 items=[1,2,3].我们要遍历这个列表我们会用下面的方式 For i in items:   Print i 首先介绍几个概念:容器,可迭代对象,迭代器 容器是一种存储数据 ...

  4. python cookbook第三版学习笔记 一

    数据结构 假设有M个元素的列表,需要从中分解出N个对象,N<M,这会导致分解的值过多的异常.如下: record=['zhf','zhf@163.com','775-555-1212','847 ...

  5. python cookbook第三版学习笔记十三:类和对象(三)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  6. python cookbook第三版学习笔记二十:可自定义属性的装饰器

    在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...

  7. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

  8. python cookbook第三版学习笔记十五:property和描述

    8.5 私有属性: 在python中,如果想将私有数据封装到类的实例上,有两种方法:1 单下划线.2 双下划线 1 单下划线一般认为是内部实现,但是如果想从外部访问的话也是可以的 2 双下划线是则无法 ...

  9. python cookbook第三版学习笔记七:python解析csv,json,xml文件

    CSV文件读取: Csv文件格式如下:分别有2行三列. 访问代码如下: f=open(r'E:\py_prj\test.csv','rb') f_csv=csv.reader(f) for f in ...

随机推荐

  1. 【Kafka】《Kafka权威指南》——写数据

    不管是把 Kafka 作为消息队列.消息.总线还是数据存储平台来使用 ,总是需要有一个可以往 Kafka 写入数据的生产者和一个可以从 Kafka读取数据的消费者,或者一个兼具两种角 色的应用程序. ...

  2. 使用PreloadJS加载图片资源

    一. 使用createjs里的LoadQueue函数实现异步加载图片,监听加载进度 1.实例对象LoadQueue加载队列对象 var queue = new createjs.LoadQueue(f ...

  3. Storyboards Tutorial 03

    这一节主要介绍segues,static table view cells 和 Add Player screen 以及 a game picker screen. Introducing Segue ...

  4. Create Data Block Based On From Clause Query In Oracle Forms

    Example is given below to create a data block based on From Clause query in Oracle Forms. The follow ...

  5. SVN环境搭建详解

      SVN服务器搭建和使用(一) Subversion是优秀的版本控制工具,其具体的的优点和详细介绍,这里就不再多说. 首先来下载和搭建SVN服务器. 现在Subversion已经迁移到apache网 ...

  6. tomcat部署不成功 Deployment failure on Tomcat 6.x. Could not copy all resources to

    解决办法: tomcat服务并没有启动.上网搜索之后发现和大家犯的是一个毛病,原来工程中我引了一个包,后来这个包被我给删除了,但是因为已经发布过这个工程了,所以classpath中就有这个包名了,这样 ...

  7. css 让两个div重叠

    做网页的时候在div里放了一个别的网页的天气插件,但是点击了会跳到广告页面的,想去网上找个禁止div点击的方法,可是发现没有,用了js的方法好像也没有成功,后来觉得还是用两个层重叠的方法来阻止点击,虽 ...

  8. uiaotumator ui測试 高速调试

    1. uiaotumator ui測试 Demo.java package uiautomatorDemo1; import java.io.File; import android.graphics ...

  9. Hadoop部署启动异常问题排查

    hadoop的日志目录(/home/hadoop/app/hadoop-2.6.4/logs) 1.hadoop启动不正常用浏览器访问namenode的50070端口,不正常,需要诊断问题出在哪里: ...

  10. VS (Visual Studio) 快捷键

    Ctrl + M + O: 折叠所有方法 Ctrl + M + M: 折叠或者展开当前方法 Ctrl + M + L:  展开所有方法