Python笔记(17)-高阶函数、装饰器

对于函数的理解

min函数可以求最小值;

In [1]: min(1,3,55,4)
Out[1]: 1

将min函数赋值给变量a;

In [2]: a=min

In [3]: a
Out[3]: <function min>

查看a,它的类型为函数,由此可见函数其实也是变量

内置高阶函数

map函数

对一组数据所有元素依次做一个函数内定义的操作

[root@centos01 python]# cat map_test.py 
#!/usr/bin/env python
# coding:utf-8
def fun(x):
    return x**2

print map(fun,[1,2,3,4])
[root@centos01 python]# python map_test.py 
[1, 4, 9, 16]

reduce函数

逐个对后面放的一组元素做函数中定义的操作

[root@centos01 python]# cat reduce_test.py 
# coding:utf-8
#description:求10的阶乘
def fun(x,y):
    return x*y

print reduce(fun,range(1,11))
[root@centos01 python]# python reduce_test.py 
3628800

filter函数

将一组数据中的元素逐个带入fun()函数判断,如果为真就输出这个元素,否则不输出

[root@centos01 python]# cat filter_test.py 
# coding:utf-8
#description:找出0~10之间所有的偶数
def fun(x):
    return x%2==0

print filter(fun,range(11))
[root@centos01 python]# python filter_test.py 
[0, 2, 4, 6, 8, 10]

sorted函数

基本语法:sorted(iterable, key=None, reverse=False)

#其中,iterable 表示指定的序列,key 参数可以自定义排序规则,按照函数中定义的内容对一组数据进行排序;reverse 参数指定以升序(False,默认)还是降序(True)进行排序。sorted() 函数会返回一个排好序的列表。

#注意,key 参数和 reverse 参数是可选参数,即可以使用,也可以忽略。

直接使用sorted排序,由于是以ASCII码表进行排序的,与我们平时认为的排序结果不大相同:

In [4]: sorted(["alic","Alex","bon","Hello","Wee"])
Out[4]: ['Alex', 'Hello', 'Wee', 'alic', 'bon']

因此,可以先把元素都转换为大写,在进行排序,就与我们平时所认为的排序方式一致了:

[root@centos01 python]# cat sorted_test.py 
# coding:utf-8
def fun(x,y):
    s1=x.upper()  #将字符串转换为大写
    s2=y.upper()
    return 0  #使用比较函数必须要有返回值,否则报错,所以这里随便给一个返回值

print sorted(["alic","Alex","bon","Hello","Wee"],fun)  #在这几个内置高阶函数里面,sorted在使用的时候是唯一一个将函数名放到一组数据后面进行使用的
[root@centos01 python]# python sorted_test.py 
['alic', 'Alex', 'bon', 'Hello', 'Wee']

也可以使用sorted key方法+lambda的方式排序(key和lambda下面有介绍):

In [4]: sorted(["alic","Alex","bon","zello","Wee"],key=lambda x:x.upper())
Out[4]: ['Alex', 'alic', 'bon', 'Wee', 'zello']

sorted key方法:

可以以一个关键字所包含的元素进行排序

In [5]: goods=[["apple",2,20],["book",5,90],["fish",3,30]]   #一个列表里面还有三个列表
In [6]: def min_one(x):        #函数作用:返回列表里的第三个元素
   ...:     return x[2]
   ...: 
In [7]: sorted(goods,key=min_one)    #以每个列表里面第三个元素为关键字进行sorted排序
Out[7]: [['apple', 2, 20], ['fish', 3, 30], ['book', 5, 90]]
In [8]: sorted(goods,key=min_one)[0][0]    #从排序结果中输出第一个列表里的第一个元素
Out[8]: 'apple'

lambda匿名函数

对于一些简单函数可以不用写出一个完整的函数形式,可以用匿名函数代替,例如lambda x:x**2就相当于

def fun(x): 

  return x**2

[root@centos01 python]# cat lambda_test.py 
# coding:utf-8
print map(lambda x:x**2,[1,2,3,4])
print reduce(lambda x,y:x*y,range(1,11))
print filter(lambda x:x%2==0,range(11))
[root@centos01 python]# python lambda_test.py 
[1, 4, 9, 16]
3628800
[0, 2, 4, 6, 8, 10]

装饰器

 闭包

[root@centos01 python]# cat test.py 
#!/usr/bin/env python
# coding:utf-8
#describe:计算从1+2+..+5的和
def lazy_sum(*args):
    def cacl_sum():
        all_sum=0
        for i in args:
            all_sum += i
        return all_sum
    return cacl_sum

f = lazy_sum(1,2,3,4,5)  #f实质是lazy_sum的返回值,即f实质是一个函数名即cacl_sum
print f()  #调用cacl_sum函数并输出返回值
[root@centos01 python]# python test.py 
15

 装饰器

如果一个项目里有多个函数都需要做一个相同的操作,如增加一行一样的注释、输出每个函数日志、函数运行时间统计、执行函数前预备处理、执行函数后清理等,可以考虑使用装饰器(decorator)。它的原理是将函数当作一个变量传入装饰器函数中,在装饰器函数中可以为其他函数批量增加一行注释等等。使用方法只需要在函数前面加上@+装饰器函数名即可

先来看一个脚本:

[root@centos01 python]# cat test1.py 
# coding:utf-8
def addInfo(fun):
    def wrapper():
        print "*******ATM*******"
        fun()
    return wrapper

def login():
    print "login..."

login=addInfo(login)  #将login函数当成变量fun传入addInfo函数,并且在addInfo函数内部执行wrapper函数的同时也执行login函数(即addInfo函数中的fun()),最后再将结果赋值给login函数
login()  #调用最新的login函数

[root@centos01 python]# python test1.py 
*******ATM*******
login...

#这个脚本实现了在一个函数(login)运行的时候增加一行注释,也就是装饰器,但是上面的语句并不美观,因此可以使用python中的语法糖来美化

[root@centos01 python]# cat test2.py 
# coding:utf-8
def addInfo(fun):
    def wrapper():
        print "*******ATM*******"
        fun()
    return wrapper

@addInfo  #装饰器的语法糖
def login():
    print "login..."

login()
[root@centos01 python]# python test2.py 
*******ATM*******
login...

#注意语法糖的位置,要在定义的函数def上方,而不是在调用函数login()那里

练习1:

输出每个函数的执行时间

# coding:utf-8
import time          #调用time模块
def Timmer(fun):
    def wrapper():
        print "********************函数开始执行********************"
        start_time=time.time()
        fun()
        end_time=time.time()
        print "********************函数执行完成********************"
        print "函数名:%s\t运行时间:%ss" %(fun.__name__,end_time-start_time)  #fun.__name__表示fun函数名称
        print "\n"
    return wrapper

@Timmer
def login():
    print "...login..."
    time.sleep(0.5)  #休眠0.5秒

@Timmer
def hello():
    print "hello world!"

login()
hello()

执行结果:

如果装饰器调用的函数需要传参怎么处理

# coding:utf-8
import time          #调用time函数
def Timmer(fun):
    def wrapper(*args,**kwargs): #增加可变参数即可,这样无论函数是否有参数传入都可以运行;为了使脚本普遍性更强一点,可以再加上关键字参数,即使没有关键字参数传入也可以运行
        print "********************函数开始执行********************"
        start_time=time.time()
        fun(*args,**kwargs)  #增加可变参数、关键字参数
        end_time=time.time()
        print "********************函数执行完成********************"
        print "函数名:%s\t函数运行时间:%ss" %(fun.__name__,end_time-start_time)  #fun.__name__表示fun函数名称
        print "\n"
    return wrapper

@Timmer
def login():
    time.sleep(0.5)
    print "...login..."

@Timmer
def world(x,y):  #函数的功能为传入的两个参数相加
    print x+y

login()
world(1,2)       #传参1和2

执行结果:

 

是否也可以对装饰器本身传参呢

只需要在原有的装饰器二重函数上再嵌套一个函数使之成为三重函数即可实现:

# coding:utf-8
def Info(pre):
    def addInfo(fun):
        def wrapper():
            print "欢迎使用XX银行ATM的[%s]系统" %(pre)
            fun()
        return wrapper
    return addInfo

@Info("登陆")
def login():
    print "登陆..."

@Info("开户")
def add_user():
    print "开户..."

@Info("转账")
def move():
    print "转账..."

@Info("存钱")
def insert():
    print "存钱..."

@Info("修改密码")
def update():
    print "修改密码..."

login()
add_user()
move()
insert()
update()

执行结果:

#和二重函数相比,三重函数的装饰器效果等同于在调用函数的时候先执行下面这条语句(以login函数为例):

login=Info("登陆")(login)

#首先执行Info("登陆"),返回的是addInfo函数,再调用返回的函数,参数是login函数,返回值最终是wrapper函数

functools.wraps

以上两种decorator(装饰器)的定义都没有问题,但还差最后一步。因为函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'login'变成了'wrapper'

[root@centos01 python]# cat test1.py 
# coding:utf-8
def addInfo(fun):
    def wrapper():
        print "*******ATM*******"
        fun()
    return wrapper

@addInfo  #装饰器的语法糖
def login():
    print "login..."

login()
print login.__name__  #打印login函数现在的__name__
[root@centos01 python]# python test1.py 
*******ATM*******
login...
wrapper  #login现在的__name__变成了wrapper
[root@centos01 python]# cat test.py 
# coding:utf-8
def Info(pre):
    def addInfo(fun):
        def wrapper():
            print "欢迎使用XX银行ATM的[%s]系统" %(pre)
            fun()
        return wrapper
    return addInfo

@Info("登陆")
def login():
    print "登陆..."

login()
print login.__name__  #打印login函数现在的__name__
[root@centos01 python]# python test.py 
欢迎使用XX银行ATM的[登陆]系统
登陆...
wrapper  #login函数现在的__name__变成了wrapper

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = fun.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

[root@centos01 python]# cat test.py 
# coding:utf-8
import functools

def addInfo(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):  #增加可变参数,这样无论函数是否有参数传入都可以运行;为了使脚本普遍性更强一点,可以再加上关键字参数,即使没有关键字参数传入也可以运行
        print "*******ATM*******"
        fun(*args,**kwargs)
    return wrapper

@addInfo  #装饰器的语法糖
def login():
    print "login..."

login()
[root@centos01 python]# python test.py 
*******ATM*******
login...

针对三重函数的装饰器用法如下:

[root@centos01 python]# cat test1.py 
# coding:utf-8
import functools

def Info(pre):
    def addInfo(fun):
        @functools.wraps(fun)
        def wrapper(*args,**kwargs):  #增加可变参数,这样无论函数是否有参数传入都可以运行;为了使脚本普遍性更强一点,可以再加上关键字参数,即使没有关键字参数传入也可以运行
            print "欢迎使用XX银行ATM的[%s]系统" %(pre)
            fun()
        return wrapper
    return addInfo

@Info("登陆")
def login():
    print "登陆..."

login()
[root@centos01 python]# python test1.py 
欢迎使用XX银行ATM的[登陆]系统
登陆...

#可以尝试着在这两个脚本最后加上print login.__name__看下,__name__参数已经变成了login本身

练习2:

请设计一个decorator,使下面脚本正常执行,并打印每个函数的执行时间:
# -*- coding: utf-8 -*-
import time, functools

# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;

@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;

f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')

# -*- coding: utf-8 -*-
import time, functools

def metric(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=fun(*args,**kwargs)
        end_time=time.time()
        print('%s executed in %s s' %(fun.__name__,end_time-start_time))
        return res  #wrapper函数中如果没有写return,默认返回值是None。由于这里的返回值脚本后面会用到(做测试),因此这里应当将fun的返回值给wrapper函数当作wrapper的返回值。否则调用过装饰器的fun函数出来后返回值都会为None
    return wrapper

# 测试
@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y

@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x * y * z

f = fast(11,22)
s = slow(11,22,33)

if f != 33:
    print('测试失败!')
elif s != 7986:
    print('测试失败!')

执行结果:

参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

练习3:

添加装饰器,所有函数都记录日志

日志格式为:时间 函数名 函数参数

# coding:utf-8
import time
def myLogger(fun):
    def wrapper(*args,**kwargs):
        fun(*args,**kwargs)
        print "%s 函数:%s 参数:%s %s" %(time.strftime("%Y-%m-%d %H:%M:%S"),fun.__name__,args,kwargs)  #依次为执行时间,函数名,可变参数,关键字参数
    return wrapper

@myLogger
def login():
    time.sleep(0.5)
    print "login..."

@myLogger
def world(x,y):
    print x+y

login()
world(1,2)

执行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值