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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python学习笔记之函数总结--高阶函数以及装饰器相关的知识,希望对你有一定的参考价值。

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)

在上面的函数体中,我们对内部函数进行了改进,其中startend分别是获取当前系统时间,中间是调用了一个函数f,所以endstart的差值就是函数f执行的时间cost,为了显示的是秒,我们对cost做了一个转化为秒数的改动,即调用total_seconds方法。

下面是程序执行后的结果:

ext_func
3.004226

首先将func函数结果传递给变量f的时候,打印了一句ext_func语句,这时函数f其实就是inn_func函数,因为func函数的返回值就是inn_func函数;调用f函数,这时我们要传递一个参数进去,而这个参数还是另一个函数,我们传递的是自定义的f2函数,它sleep3秒,所以两个查找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

以上是关于python学习笔记之函数总结--高阶函数以及装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python全栈学习总结六:装饰器

《Python学习之路 -- Python基础之装饰器》

Python学习笔记012——装饰器

Python高阶函数之 - 装饰器

Python基础笔记:函数式编程:高阶函数返回函数匿名函数装饰器偏函数

Python函数作用域嵌套函数闭包函数高阶函数及装饰器的理解