高阶函数&&装饰器

高阶函数&&装饰器

高阶函数
  • 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 22:58
下一篇 2017-10-24 21:04

相关推荐

  • 文本处理工具及grep

    在日常的linux运维工作当中,我们经常要在一些文本当中抽取过滤出我们所需要的信息,从而达到我们的需求,需要特定的文本处理工具来帮我们完成此类操作 本章节主要讲解的内容有: 文件查看:cat 分页查看文本:less、more 抽取文件特定行数:head、tail 抽取文本特定列:cut 合并文本:paste 文本统计:wc 文本排序并统计:sort、uniq…

    Linux干货 2016-08-08
  • CentOS6.7上编译安装MariaDB

    CentOS6.7上编译安装MariaDB

    系统运维 2016-06-03
  • 马哥教育网络班21期+第七周博客作业

    1、创建一个10G分区,并格式为ext4文件系统;    (1) 要求其block大小为2048, 预留空间百分比为2, 卷标为MYDATA, 默认挂载属性包含acl;    (2) 挂载至datamydata目录,要求挂载时禁止程序自动运行,且不更新文件的访问时间戳 [root@ns1 ~]# m…

    Linux干货 2016-08-24
  • Linux上获取命令帮助信息及man文档划分

    1.Linux上获取命令帮助信息的多种途径 Linux上获取命令帮助信息有多种途径,但不同的命令类型获取帮助信息也有不同,Linux命令类型主要分为两种: (1)内部命令:指的是集成于Shell解释器程序(如Bash)内部的一些特殊指令,也成为内建(BuiltIN)指令。 内部命令属于Shell的一部分,所以并没有单独对应的系统文件,只要Shell解释器被运…

    Linux干货 2016-10-30
  • PHP高效率写法(详解原因)

    1.尽量静态化:    如果一个方法能被静态,那就声明它为静态的,速度可提高1/4,甚至我测试的时候,这个提高了近三倍。   当然了,这个测试方法需要在十万级以上次执行,效果才明显。   其实静态方法和非静态方法的效率主要区别在内存:静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,所以静…

    Linux干货 2015-05-28
  • Linux用户和组管理

    进程以其发起者的身份运行; 进程对文件的访问权限,取决于发起此进程的用户的权限;   系统用户:为了能够让那后台进程或服务类进程以非管理员的身份运行,通常需要为此创建多个普通用户;这类用户从不用登录系统;     groupadd命令:添加组   groupadd [选项] group_name   -g G…

    Linux干货 2016-11-21