高阶函数&&装饰器

高阶函数&&装饰器

高阶函数
  • 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

相关推荐

  • PHP的类自动加载机制

    在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么做会产生大量的require或者include方法调用,这样不因降低效率,而且使得代码难以维护,况且require_once的代价很大。 在PHP5之前,各个…

    Linux干货 2015-04-10
  • tcp socket文件句柄泄漏

    今天发现有台redis机器上出现socket个数告警,这是很奇怪的现象。因为一台redis服务器上就部署了几个redis实例,打开的端口应该是有限。 1、netstat显示的tcp连接数正常 netstat -n | awk '/^tcp/ {++state[$NF]} END …

    Linux干货 2016-04-13
  • 【linux】正则表达式之grep、egrep、元字符

    正则表达式:     又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。是一类字符所书写的模式,其中许多字符(元字符)不表示其字面意义,而是表达控制或通配等功能。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符…

    Linux干货 2015-04-01
  • MySQL入门书籍和方法分享

    原文:http://cenalulu.github.io/mysql/mysql-book-for-newbie/           作者: 卢钧轶     本文罗列了一些适用于MySQL及运维入门和进阶使用的书籍。 背景:各大论坛上总是…

    Linux干货 2015-04-09
  • 第三周作业

      1. who |cut -d ” ” -f1|uniq 2.who |head -1 3.cat /etc/passwd | cut -d: -f7|uniq -c |sort -n|tail -1|grep -o “/[[:alnum:]].*” 4. cat /etc/passwd |sort…

    2017-12-16
  • 目录,inode学习笔记

    目录,inode学习笔记 1. 关于目录,文件,数据块 对于使用计算机的人而言,经常有一种 错误的认知:目录(或者说,文件夹)里面存放着文件。实际上,目录里面并不存放文件,以及文件数据。 实际上,目录是一个特殊的文件,针对这个特殊的文件也存在一些特殊的规则,比如利用命令cp /dev/null <your directory>…

    Linux干货 2017-04-01