高阶函数&&装饰器

高阶函数&&装饰器

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

相关推荐

  • ntp时间服务器搭建实例

    ntp时间服务器采用stratum分级架构来处理时间同步;举例说明:你搭建了一台ntp服务器,然后同步的server为stratum-1,你的ntp则为stratum-2,你的下级ntp则为tratum-3。依此类推,最多为15层。 1.ntp server安装: [root@localhost ~]# yum -y …

    Linux干货 2015-11-10
  • 系统服务之LVS 集群

    Linux集群(Cluster) 一.概论 1.定义     Cluster:计算机集合;     linux集群,多台Linux主机为解决某个特定问题组合起来形成的单个系统;     由于现代化业务上线的需求, 单服务器已经不能…

    Linux干货 2016-10-28
  • 学习Linux的第一周 之screen

         1.来到马哥的感想:      已经接触Linux已经一周,在紧张的而又条理的学习当中,我学到了很多东西单说量我就很佩服自己,在大学的两年当中已经快把自己学习新知识的渴望忘记了!来到马哥这短短一周我感觉我真的长大了很多,我把手机王者荣耀卸载了。只想对自己说加油坚持下去。…

    Linux干货 2017-07-16
  • 磁盘管理——MBR与GPT的分区

    一、硬盘的结构 这里讲的主要是网上所谓的老式磁盘,它是由一个个盘片组成的,我们先从个盘片结构讲起。如下图所示,图中的一圈圈灰色同心圆为一条条磁道,从圆心向外画直线,可以将磁道划分为若干个弧段,每个磁道上一个弧段被称之为一个扇区(图中蓝色部分)。扇区是磁盘的最小组成单元,通常是512字节           &…

    Linux干货 2016-09-02
  • ——-史上最全的Vim命令 ——-vim命令 /正则表达式 /搜索 /定位 /分屏

    一,vim vim编辑器是vi的进阶版,是一个静态文本编辑器。它的强大不逊色于任何最新的文本编辑器,
    对Unix及Linux系统的任何版本,vi编辑器是完全相同的。Vi也是Linux中最基本的文本…

    2017-11-26
  • 系统自动化安装和SELinux

    一、知识整理 1、anaconda系统安装程序:默认图形启动; 使用光盘启动,在选择模式界面tab键在后面增加text或按下ESC键,输入lnux text进入字符界面安装。 2、创建kickstart文件: 直接手动编辑:依据模板修改,/root目录下的anaconda.cfg 使用创建工具创建:system-config-kickstart,图形化工具:…

    Linux干货 2016-09-26