您的位置:首页 > 编程语言 > Python开发

python学习笔记之函数总结--高阶函数以及装饰器

2016-06-21 15:22 766 查看
python学习笔记之函数总结--高阶函数以及装饰器

Python特点: 1、不是纯函数式编程(允许变量存在); 2、支持高阶函数(可以传入函数作为变量); 3、支持闭包(可以返回函数); 4、有限度的支持匿名函数;高阶函数: 1、变量可以指向函数; 2、函数的参数可以接收变量; 3、一个函数可以接收另一个函数作为参数;
下面我将示例一些函数的写法以及使用,并说明python中函数的特性:
1、基本的高阶函数示例:
#!/usr/bin/env python
def func():
print "ext_func"
def inn_func():
print "inn_func"
return inn_func()
print func()
在func函数中我们首先输出的是ext_func表明这是在外部函数中输出的语句,随后在里面定义了内置函数inn_func,在内部函数中输出的是inn_func,最后在外部函数中return返回内部函数的结果
下面是程序运行的结果:
ext_func
inn_func
None
运行程序,首先调用外部函数func,输出语句ext_func,然后return内部函数,内部函数输出inn_func,函数func调用已经完成,返回结果为inn_func的返回值,其返回值为None,我在最后调用时,是print fun(),即打印func函数的返回值,它返回inn_func的返回值,即为None.

2、我们在上一个程序的基础上,进行改进,在调用时如下:
#!/usr/bin/env python
def func():
print "ext_func"
def inn_func():
print "inn_func"
return inn_func

f = func()
f()
我们现将func()的返回值传递给变量f,再调用f函数
结果如下:
ext_func
inn_func
通过结果,可以看出,第一次是将func()的返回值赋给了f,而func()的返回值就是return inn_func,即把inn_func这个内部函数传递给了变量f,所以在后面调用f()时,就相当于调用函数inn_func(),所以只有两个print 语句输出了。

3、下面是一个对部函数的改进,调用了两个模块time和datetime,简单地实现了一个计时:
#!/usr/bin/env python
import time
import datetime
def func():
print "ext_func"
def inn_func(f):
start = datetime.datetime.now()
f()
end = datetime.datetime.now()

cost = end - start
print cost.total_seconds()

return inn_func

def f2():
time.sleep(3)
f = func()
f(f2)
在上面的函数体中,我们对内部函数进行了改进,其中start和end分别是获取当前系统时间,中间是调用了一个函数f,所以end和start的差值就是函数f执行的时间cost,为了显示的是秒,我们对cost做了一个转化为秒数的改动,即调用total_seconds方法。
下面是程序执行后的结果:
ext_func
3.004226
首先将func函数结果传递给变量f的时候,打印了一句ext_func语句,这时函数f其实就是inn_func函数,因为func函数的返回值就是inn_func函数;调用f函数,这时我们要传递一个参数进去,而这个参数还是另一个函数,我们传递的是自定义的f2函数,它sleep了3秒,所以两个查找cost也是3秒,和预期效果一样。

特别要注意的是,调用一个函数的内部函数,有可能是需要传递参数进去了,可能在外部函数传递参数,也可能是在内部函数进行传递参数,一定要注意到底在哪块传递参数,传的参数是什么,在里面哪里调用了,以及在最后使用函数时,如何根据所写的不同,要具体怎样去调用函数。

4、下面在看一个改进后的程序示例:
#!/usr/bin/env python
import time
import datetime

def func(fc):
print "ext_func"
def inn_func(*arg,**kwargs):
start = datetime.datetime.now()
fc(*arg,**kwargs)       #f2(arg)
end = datetime.datetime.now()

cost = end - start
print cost.total_seconds()

return inn_func

def f2(arg):
print arg
time.sleep(arg)

def f3(arg1,arg2):
print arg1,arg2
time.sleep(5)

def f4():
time.sleep(6)

f = func(f2)
f(4)
在这个程序中,我们传递了参数,首先是对内部函数的参数传递,有时会传递进来一个函数在里面调用,而传递进来的函数有可能也是需要传递参数的,所以我们就要处理传递函数和传递函数参数的问题,一般我们将函数在外部函数中进行传递进来在里面进行调用,而函数所需要的参数,我们可以在内部函数中进行传递,比如上面这个程序,外部函数传递进来的fc在内部函数中进行调用,而传进来函数的参数在内部函数中进行传递;
为了能将所有情况下的参数全部传递进来,所以在内部函数传参时,采用了*arg和**kwargs以保证所有类型的传参都能成功,在具体fc函数调用时,则需要进行解序列传参进去使用,最后的几个函数就是为了进行测试不同参数传参的问题。
下面是程序运行的结果:
ext_func
4
4.005157

可以看到传递了f2函数给外部函数func,将其返回值inn_func传递给f,调用f函数时,还需要传递一个参数进去,这是根据f2函数内部结构得到的,传递的值是4,所以结果和预期一致。

5、下面是一个练习脚本示例:
#!/usr/bin/env python

def func1(fc):
print "This is a function of boss."
print type(fc)
def func2(*arg):
print "This is a function in function."
print arg
fc(*arg)
print fc(*arg)

return func2

def f1(x,y):
return x*y

f = func1(f1)
f(3,5)
结果如下:

This is a function of boss.
<type 'function'>
This is a function in function.
(3, 5)
15


6、装饰器的一些示例与作用说明:
#!/usr/bin/env python
import time
import datetime
def func(fc):
print "ext_func"
def inn_func(*arg):
start = datetime.datetime.now()
fc(*arg) #f2(arg)
end = datetime.datetime.now()

cost = end - start
print cost.total_seconds()

return inn_func

@func
def f2(arg):
print arg
time.sleep(arg)
@func
def f3(arg1,arg2):
print arg1,arg2
time.sleep(5)

@func
def f4():
time.sleep(6)

#f = func(f4)
#f()
#f2(3)          #fc = func(f2);fc(3)
f3(3,5)
在这段程序中,主要是使用了装饰器,即@func就是下面定义函数的装饰器
在执行的时候直接调用要传递的参数就可以实现和上面一样的功能,这是因为装饰器解释给解释器,自动执行了原来需要手动传递函数的那一步,即fc = func(f2);所以你执行了f2(3),就相当于执行了fc =func(f2); f2(3)两步。

注意:装饰器要在函数定义前进行装饰,每装饰一个函数,解释器都会执行fc = func(f2)这一步,在下面的结果处你就可以看到,每个解释器只能解释一个函数,相同的需要都写在前面。
下面是程序执行的结果:
ext_func
ext_func
ext_func
3 5
5.005236
可以看到,在程序中值调用了一次函数,就是f3(),可是ext_func打印了三遍,这是因为装饰器的原因,自动执行了一步,但并没有执行后面的内部函数调用,所以只打印ext_func这句话,而f3()则进行了调用,且传递了参数为5,所以打印结果是时间。和预期效果一致。

7、练习示例:
#!/usr/bin/env python
def check_priv(fc):

defwrap(*arg,**kwargs):
ifusername == 'admin':
fc(*arg,**kwargs)
else:
print"Permission denied"
returnwrap

username = "admin"

@check_priv
def f6(arg):
printarg
print"restart nginx...",arg

f6('ok')
结果如下:
ok
restart nginx... ok


8、装饰器结合functools返回函数自身属性(函数名):
#!/usr/bin/env python
import functools
def func_boss(func):
print "This is a function boss"
@functools.wraps(func)
def func_son(*arg,**kwargs):
print "This is a functionson"
func(*arg)
print "Ending"
return func_son

S = 1
@func_boss
#func_test = func_boss(func_test)
def func_test(*arg):
print "This is a function test"
#S = 1
global S
for i in range(len(arg)):
S = S*arg[i]
print S
return S

#func_test(1,2,3,4)
#func_test()

a = func_test
print a.__name__

在上面的程序中,我们看到func_boss作为装饰器装饰了函数func_test,这样等于是执行了func_test =func_boss(func_test)这一步,这我们都在上面进行了说明;
那么如果我调用函数func_test时,如果要获取它的名字,返回的结果是什么呢,通过测试发现它返回的是内部函数func_son,这并不是我们想要的结果,因为执行了装饰器的功能,所以函数的属性已经变成了内部函数func_son的,因为func_test就是接受了装饰器的结果,即return func_son。
这时,如果我们想要原来函数的属性的话,可以借助functools模块,首先应该导入这个模块,在装饰器函数里面进行调用,这里是在内部函数func_son定义之前加一个解释器@functools.wraps(func),它的功能是将func函数的属性赋给高阶函数中的内部函数(又称为wrap函数),这样执行的结果就是我们想要的。

不加@functools.wraps(func)的结果如下所示:
This is a function boss
func_son


加@functools.wraps(func)的结果如下:
This is a function boss
func_test
和我们想要的结果一样,获取到的是func_test函数名

9、yield的用法及示例(一):
!/usr/bin/env python
def odd():
n= 1
print"hello yield"
whileTrue:
#print"before yield"
yieldn
print"after yield"
n+=2

for i in odd():
printi
ifi >= 101:
break
yield出现的函数就会有一个迭代器,它返回的值是依次取的,当你需要用的时候才会给你,不会马上把所有的值都取完,这样太费内存,浪费资源,yield就是返回一个值就会停住,进行等待,当你下一次需要值时,它才执行去返回下一个值,否则就一直停在那,这样做的好处是节省资源。
o = odd()
print "======="
print o.next()
print o.next()
print o.next()
print o.next()
获取yield的返回值(迭代器的返回值)可以使用.next()方法进行获取,获取完所有值就不再有值了,每获取一个值迭代器指向的位置就会变化一次,直到取完所有值。

10、yield用法及示例(二):
#!/usr/bin/env python
def gen():
value= 0
whileTrue:
recv= yield value
ifrecv == 'e':
break
value= "gen:%s" % recv

g = gen()

print g.send(None)  #g.next()
print g.send('aaaa')
print g.send('bbbb')
print g.send('cccc')
print g.send('dddd')
在上面的程序中,我们主要是要介绍send()方法,是怎样使用的,在函数定义中,我们看recv = yield value这一句,这一句是可以给yield传进来值的,即yield是可以接受send过来的值。
由于yield的特殊性,它在返回一个值的时候就已经停下来了,所以第一次并没有接受send的值,下面调用时的第一个g.send(None),相当于是g.next(),这就是让yield在往下进行一次取值,跳过第一次返回值后就停止的状态,接下来就是一次接受send的值,然后进行替换value值后返回,为了测试可在前后加一些必要的测试语句查看有什么不同,帮助理解。

下面是程序执行的结果:
0
gen:aaaa
gen:bbbb
gen:cccc
gen:dddd


11、下面是改进以后的示例,加入了一些别的功能,可以帮助理解yield和send方法的使用:
#!/usr/bin/env python
def sgen():
value= 0
whileTrue:
rec= yield value
ifrec == "quit":
value= 'please input "q" to quit'
elifrec == "hello":
#print"hello,nihao"
value= "hello,@you"
else:
value= "what are you say?"
s = sgen()
s.send(None)          #跳出第一次停等状态
while True:
TEXT= raw_input("please input:")
ifTEXT == "q":
break
prints.send(TEXT)
结果如下:
please input:ok
what are you say?
please input:hello
hello,@you
please input:quit
please input "q" to quit
please input:q


本文出自 “ptallrights” 博客,请务必保留此出处http://ptallrights.blog.51cto.com/11151122/1791393
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: