高阶函数&&装饰器

高阶函数&&装饰器

高阶函数
  • first class object
    • 函数在python中是一等公民
    • 函数也是对象,可调用的对象
    • 函数可以作为普通比变量、参数、返回值
  • 高阶函数
    • 数学概念:y = g(f(x))
    • 在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数
    • 接受一个或多个函数作为参数
    • 输出一个函数,return 函数
  • 举例:
def counter(base):
def inc(step=1):
#nonlocal base#不加此行会有什么影响?调用时会提示base无定义
base += step
return base
return inc
foo = counter(10)
foo()#将变量声明成nonlocal即可调用
UnboundLocalError Traceback (most recent call last)
<ipython-input-336-624891b0d01a> in <module>()
—-> 1 foo()
<ipython-input-334-2a9fcd38455a> in inc(step)
2 def inc(step=1):
3 #nonlocal base
—-> 4 base += step
5 return base
6 return inc
UnboundLocalError: local variable ‘base’ referenced before assignment
注意:大的对象都是在堆里面创建
f1 = counter(5)和f2 = counter(5),相等吗?
单纯counter是一样的,但是由于返回值时一个新的对象inc,而inc相当于常量在堆中创建f1 、 f2在堆中为不同的空间
自定义sort函数
#代码一:
def sort(iterable):#定义函数
ret = []#定义排序的空列表
for x in iterable:#循环列表
for i,y in enumerate(ret):
if x > y:#找到大的就地插入。如果x<y则升序
ret.insert(i,x)#降序
break
else:#不大于,说明值小则尾部追加
ret.append(x)
return ret
print(sort([4,3,4,5,2,1,9,8]))
#代码二:用一个参数控制顺序
def sort(iterable,reverse=False):#定义函数
ret = []#定义排序的空列表
for x in iterable:#循环列表
for i,y in enumerate(ret):
flag = x > y if not reverse else x < y#默认是降序,去掉not则为升序,此方法是通过传参实现控制
if flag:#找到大的就地插入。如果x<y则升序
ret.insert(i,x)#降序
break
else:#不大于,说明值小则尾部追加
ret.append(x)
return ret
print(sort([4,3,4,5,2,1,9,8]))
#代码三:函数参数实现:
def sort(iterable,fn=lambda a,b:a<b):#使用匿名函数传参
ret = []
for x in iterable:
for i,y in enumerate(ret):
#fn = lambda x,y:x>y#此方法也阔以
if fn(x,y):#函数调用获取返回值,控制参数即可
ret.insert(i,x)
break
else:
ret.append(x)
return ret
print(sort([4,3,4,5,2,1,9,8]))
  • sorted(iterable[,key][,reverse])排序
    • 返回一个新的列表,对一个可迭代带向的所有元素排序,排序规则为key定义的函数,reverse表示是否排序翻转
    • sorted(lst,key=lambda x:6-x)#返回新列表,此处并没有修改列表的值而是通过值来决定是大于还是小于,所以此处是倒序排列,大值变小小值变大。
    • lst.sort(key=lambda x:6-x)#就地修改
lst = [4,3,4,5,2,1,9,8]
sorted(lst,key=lambda x:6-x)
print(lst)
lst.sort(key=lambda x:6-x)
print(lst)
[4, 3, 4, 5, 2, 1, 9, 8]
[9, 8, 5, 4, 4, 3, 2, 1]#结果可证实上面理论部分
filter(function,iterable)
* 过滤可迭代对象元素,返回一个迭代器。惰性求值
* function一个具有一个参数的函数,返回bool
filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123])
<filter at 0x7fe1840e9a20>
list(filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123]))
[9, 150, -3, 78, 123]#此处用列表转换可以直接获取结果
map(function,*iterables)—>map object
  • 对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器
list(map(lambda x:2*x+1,range(5)))
[1, 3, 5, 7, 9]
dict(map(lambda x:(x%5,x),range(500)))
{0: 495, 1: 496, 2: 497, 3: 498, 4: 499}#显示这个结果的原因是key值不允许重复
柯里化Currying
  • 定义:将原来接受两个参数的函数变成新的接受一个参数的函数过程,新的函数返回一个以原有第二个参数为参数的函数
  • z = f(x,y)转换成z = f(x)(y) 形式虽不同但是结果是一样的
#举例:将加法函数柯里化
def add(x,y):
return x+y
add(4,5)#原始加法函数
def add(x):
def add2(y):
return x+y
return add2#重点:让add的返回值去调用add2这样就可以关联xy
add(4)(5)#柯里化加法函数
  • 通过嵌套函数就可以把函数转化成柯里化函数

装饰器
  • 应用场景:
    • 一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
    • def add(x,y):
      • retrun x + y
    • 现增加输出功能
    • def add(x,y):
      • print(“call add,x + y”)
      • return x+y
    • 上面的函数是完成了需求,但是有以下缺点:
      • 打印语句的耦合太高
      • 加法函数属于业务功能,而输出信息的功能属于非业务代码,不该放在业务函数加法中,这属于代码侵入
  • 下列代码做到了业务功能分离,但是fn函数调用参数是个问题
def add(x,y):
return x+y
def logger(fn):
print(“begin”)
x = fn(4,5)##参数写到代码里面不灵活
print(“end”)
return x
print(logger(add))
  • 通过柯里化可以使用传参灵活实现代码控制
def add(x,y):
return x+y
def logger(fn):#此处也可写成(fn,*args,**kwargs)来完成传参需求
def biu(x,y):
print(“begin”)
x = fn(x,y)
print(“end”)
return x
return biu
print(logger(add)(4,5))
  • 装饰器是python提供的语法糖
def logger(fn):
def biu(x,y):
print(“begin”)
x = fn(x,y)
print(“end”)
return x
return biu
@logger#装饰器将下方紧邻的函数作为参数传递给装饰器函数,add为wrapped,fn为wrapper。等价于 add = logger(add),所以add是biu的对象
def add(x,y):
return x+y
print(add(34,32))
  • 装饰器(无参)
    • 他是一个函数
    • 函数作为他的形参
    • 返回值也是一个函数
    • 可以使用@functionname方式,简化调用
    • 装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
  • 装饰器造成的问题:
    • 原函数对象的属性都被替换了,而使用装饰器,我们需要查看被装饰函数的属性如何解决?
#解决方法一:定义一个新函数,作用:将被修饰函数的属性重新赋予修饰函数
def copy_properties(src,dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
‘I am wrapper’
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
return x
copy_properties(fn,wrapper)
return wrapper
@logger#add = logger(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
return x+y
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
#通过copy_properties函数将被包装函数的属性覆盖到包装函数
#凡是被装饰器的函数都需要复制这些属性,这个函数很通用
#可以将复制属性的函数构建成装饰器函数,带参装饰器如下方法:
#解决方法二:带参装饰器:
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
return x
return wrapper
@logger#add = logger(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
return x+y
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
  • 需求:获取函数的执行时长,对时长超过阈值的函数记录一下
import datetime
import time
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(duration):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘so slow’) if delta > duration else print(‘so fast’)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6))
  • 带参装饰器:
    • 他是一个函数
    • 函数作为他的形参
    • 返回值是一个不带参的装饰器函数
    • 返回@functionname(参数列表)方式调用
    • 可以看做在装饰器外层又加了一层函数
  • 将记录的功能提取出来,这样就可以通过外部提供的函数来灵活控制
import datetime
import time
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(6)
return x+y
print(add(5,6))

funtools模块
  • functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENMENTS,updated=WRAPPER_UPDATES)
    • 类似copy_properties功能
    • wrapper包装函数,wrapped被包装函数
    • 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性__module__,__name__,__qualname__,__doc__,__annotations__模块名、名称、限定名、文档、参数注解
    • 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
    • 增加一个__wrapped__属性,保留着wrapped函数
import datetime
import time
import functools
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return functools.update_wrapper(wrapper,fn)
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)
##使用装饰器调用functools
import datetime
import time
import functools
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def _logger(fn):
@functools.wraps(fn)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88039

(0)
Thunk_LeeThunk_Lee
上一篇 2017-10-23
下一篇 2017-10-24

相关推荐

  • 文本编辑秘籍之vim宝典

    一、初识Vim Vim是从 vi 发展出来的一个文本编辑器。其功能非常强大,熟练掌握vim的常用操作和快捷操作能让我们从繁杂的文本处理任务中杀出血路,在运维的道路上愉快前行。 vim使用简单命令组合来完成复杂操作,同时也支持基本正则表达式。 二、拨开vim迷雾: 2.1 使用vim打开一个文件: 如果该文件存在,文件被打开并显示内容;如果该文件不存在,当编辑…

    Linux干货 2016-08-10
  • 计算机之路及初识linux

    写在前面:     很抱歉,上周的事儿,拖到这周,也是个半成品,加班就不多说了,每个人都很忙,总要自己找时间。本计划这个周末好好补补,无奈身不由己,刚刚回来。    还好昨夜先起了个初稿,总算是有些东西可以交代给自己,想到马哥说的,完成远比完善重要,虽然来不及完善,先发出来吧,有了框架,至少知道自己做过什么。尽快排版吧…

    Linux干货 2016-12-05
  • linux磁盘管理之LVM

                        LVM  LVM是 Logical Volume Manager(逻辑卷管理)的简写,它是Linux环境下对磁…

    Linux干货 2016-09-09
  • 梦想走向现实的地方

    曾经羡慕打字快的同学指尖能在键盘上优雅地跳舞,于是我有努力练习打字。 曾经羡慕同事能很神奇地把一个交换机的端口关闭,让那端口下面的人都不能上网,于是我努力学习交换机、路由。 现在我羡慕一个人能管很多服务器的人,于是我开始学习linux了。 努力成为一个也能管很多服务器的人,因为我也想别人叫我运维工程师

    Linux干货 2016-10-25
  • 文件搜索者-find命令详解

    1. 文件查找:          在linux系统中由于文件的众多,往往需要在众多的文件当中查找某一个文件,如果时间一长,很难记得文件存放至何处,不过,这一点,你不比担心,因为开发人员为我们提供了强大的文件搜索工具,下面将介绍两款常用的文件查找工具locate,和find,这两…

    Linux干货 2016-08-15
  • N24_阿龙弟弟 学习计划/目标/宣言

    嗨,大家好,这是我的第一篇文章。很高兴来到马帮门徒这个大家庭。 学习计划:没有自己的计划,跟着马哥课程的进度来吧,以周为单位,确保每周任务完成; 学习目标:掌握应有的运维能力,做一名合格的Linux运维工程师,提升自我价值,过更好的生活; 学习宣言:Be a better man(Not Only Linux)!

    Linux干货 2016-10-25