高阶函数
- First Class Object
- 函数在Python中是一等公民
- 函数也是对象,可调用(callable)的对象
- 函数可以作为普通变量、参数、返回值等等
- 高阶函数
- 数学概念y=g(f(x))
- 在Python中,高阶函数应该满足下列至少一个条件
- 接受一个或者多个函数作为参数
- 输出一个函数
- 计数器
def counter(base): def inc(step=1): base += step return base return inc 分析: - 函数counter是一个高阶函数,因为它返回了一个函数对象 - 上述函数的问题在于内层函数使用外层函数的本地变量,但同时有修改它,所以调用是会报错UnboundLocalError,想要更改只需要使用关键字nonlocal在要改变前声明它是外层函数的变量 - 调用应该使用counter(base)(step) - f1 =counter(5)和f1 = counter(5)是不相等的,因为上述调用是两次调用,这样他们的ID就是不一样的,所以不相等
自定义sort函数
- 排序问题:仿照内建函数sorted,自行实现一个sort函数(不使用内建函数),能够为列表元素排序
- 思路
- 内建函数sorted是返回一个新的列表,可以设置升序或降序,可以设置一个排序函数。自定义的函数也应该有类似功能
- 新建一个列表,遍历原列表,和新列表的值依次比较决定如何插入到新列表中
- 函数实现1。判断以下代码是如何排序,还能优化么?
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([1,7,2,9,3,6,8,4,5]))
- sort函数实现2。用一个参数控制顺序
def sort(iterable,reverse=False): ret = [] for x in iterable: for i,y in enumerate(ret); flag = x > y if reverse else x < y if flag: ret.insert(i,x) break else: ret.append(x) return ret print(sort([1,7,2,9,3,6,8,4,5]))
- sort函数实现3。
def sort iterable,fn=lambda a,b:a>b): ret = [] for x in iterable: for i,y in enumerate(ret); if fn(i,x): #函数返回值是bool型 ret.insert(i,x) break else: ret.append(x) return ret print(sort([1,7,2,9,3,6,8,4,5]))
内建函数-高阶函数
- sorted(iterable[,key][,reverse])排序
- 返回一个新列表1,对一个可迭代对象的所有元素排序,排序规则为可以定义的函数,reverse表示是否排序翻转
- sorted(lst,key=lambda x:6-x) #返回新列表
- list.sort(key=lambda x:6-x) #就地修改
- filter(function,iterable)
- 过滤可迭代对象的元素,返回一个迭代器
- function是一个具有一个参数的函数,返回bool
- 例如,过滤除被3整除的数字:list(filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123]))
- map(function,*iterables) –> map object
- 对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器
- 例子:
- list(map(lambda x:2*x+1,range(5)))
- dict(map(lambda x:(X%5,x),range(500)))
柯里化
- 指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数
- z = f(x,y)转换成z = f(x)(y)的形式
- 举例
def add(x,y): return x+y 转换如下: def add(x): def _add(y): return x+y return _add add(5)(6) 通过嵌套函数就可以吧函数转换称柯里化函数
装饰器
- 需求
- 一个加法函数,想要加强它的功能,能够输出被调用过以及调用的参数信息
def add(x,y): return x+y 增加信息输出功能 def add(x,y): print('call add,{}+{}'.format(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,*args,**kwargs): #这里*args和**kwargs代表可变参数 print('begin') x = fn(*args,**kwargs) #*args,**kwargs在这表示参数解构 print('end') return x print(logger(add,5,y=50)) 柯里化 def add(x,y): return x + y def logger(fn): def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return _logger print(logger(add)(5,y=50)) #最后也可以这么写 # add = logger(add) # print(add(x=5,y=10))
- 装饰器语法
def logger(fn):
def _logger(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return _logger
@logger #等价于add= logger(add),这就是装饰器语法
def add(x,y):
return x+y
print(45,50)
- 上面的语法是无参装饰器
- 它是一个函数
- 函数作为他的形参
- 返回值也是一个函数
- 可以使用@functionname方式,简化调用
- 装饰器是高阶函数,但装饰器对传入函数的功能的装饰(功能增强)
文档字符串
- Python中文档字符串Documentation Strings
- 在函数体语句块的第一行,且习惯是多行的文本,所以多使用三引号
- 惯例是首字母大写,第遗憾写概述,空遗憾,第三行写详细描述
- 可以是使用特殊属性__doc__访问这个文档
def add(x,y):
'''This is a function of addition'''
a = x+y
return a
print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
print(help(add))
使用装饰器的副作用
- 使用装饰器后原函数对象的属性都被替换了,而使用装饰器,希望查看原函数的属性,如何解决
- 可以自己定义一个函数将被包装函数的属性覆盖掉包装函数
def copy_properties(src,dst): #自定义复制属性的函数 dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x copy_properties(fn,_logger) #函数调用,复制被包装函数属性覆盖包装函数属性 return _logger @logger def add(x,y): return x+y print(45,50)
- 凡是被装饰的函数都需要赋值这些属性,这个函数很通用
- 可以将被复制属性的函数构建成装饰器函数,带参装饰器
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) #_logger = copy_properties(fn)(_logger) def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return _logger @logger def add(x,y): """this is """ return x+y print(45,50)
带参装饰器
- 它是一个函数
- 函数作为他的形参
- 返回值是一个不带参的装饰器函数
- 使用@functionname(参数列表)方式调用
- 可以看作在装饰器外层又加了一层函数
def logger(duration):
def _logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta =(datetime.datetime.now()-start).total_seconds()
print('so slow') if delta >duration else print('so fast')
return ret
return wrapper
return _logger
@logger(5)
def add(x,y):
time.sleep(3)
return x+y
print(add(5,6))
- 将记录的功能提取出来,就可以通过外部提供的哈数来灵活的控制输出
def logger(duration,func=lambda name,duration:print('{}took{}s'.format(name,duration))):
def _logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta =(datetime.datetime.now()-start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
print('so slow') if delta >duration else print('so fast')
return ret
return wrapper
return _logger
functools模块
- functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
- 类似copy_properties功能
- wrapper包装函数,wrapped被包装函数
- 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
- 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
- 增加一个__wrapped__属性,保留着wrapped函数
- functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)和上个方法用法基本相同
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88009