python中的函数式编程与装饰器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python中的函数式编程与装饰器相关的知识,希望对你有一定的参考价值。
- 把计算视为函数而非指令
- 纯函数式编程,不需要变量,没有副作用,测试简单
- 支持高阶函数,代码简洁
- 不是纯函数式编码:允许有变量
- 支持高阶函数:函数也可以作为变量传入
- 支持闭包:有了闭包就能返回函数
- 有限度地支持匿名函数
- 变量可以是指向函数
- 函数的参数可以接收变量
- 一个函数可以接收另一个函数作为参数
- 能接收函数作为参数的函数就是高阶函数
#函数作为参数示例
def add(x,y,f):
return f(x)+f(y)
add(-5,9,abs)
在2.1小节中,我们讲了高阶函数的概念,并编写了一个简单的高阶函数:
def add(x, y, f):
return f(x) + f(y)
如果传入abs作为参数f的值:
add(-5, 9, abs)
根据函数的定义,函数执行的代码实际上是:
abs(-5) + abs(9)
由于参数 x, y 和 f 都可以任意传入,如果 f 传入其他函数,就可以得到不同的返回值。
示例,利用add(x,y,f)函数,计算
import math
def add(x, y, f):
return f(x)+ f(y)
print(add(25,9, math.sqrt))
def format_name(s):
a = s[0].upper()
b = s[1:].lower()
return a+b
print(list(map(format_name,[‘adam‘,‘LISA‘,‘barT‘])))
任务
Python内置了求和函数sum(),但没有求积的函数,请利用recude()来求积:
输入:[2, 4, 5, 7, 12]
输出:2*4*5*7*12的结果
from functools import reduce
def prod(x, y):
return x*y
print(reduce(prod,[2,4,5,7,12]))
任务
请利用filter()过滤出1~100中平方根是整数的数,即结果应该是:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
import math
def is_sqr(x):
a = math.sqrt(x)%1
if a ==0.0:
returnTrue
else:
returnFalse
l = filter(is_sqr, range(1,101))
print(list(l))
任务
对字符串排序时,有时候忽略大小写排序更符合习惯。请利用sorted()高阶函数,实现忽略大小写排序的算法。
输入:[‘bob‘, ‘about‘, ‘Zoo‘, ‘Credit‘]
输出:[‘about‘, ‘bob‘, ‘Credit‘, ‘Zoo‘]
def cmp_ignore_case(s1, s2):
if s1.lower()> s2.lower():
return1
elif s1.lower()< s2.lower():
return-1
else:
return0
print sorted([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘], cmp_ignore_case)
#这段代码在Python2.7可以运行,python3.x之后sorted函数不支持自定义比较函数,在python3.x中sorted函数的定义为
#sorted(iterable, /, *, key=None, reverse=False)
#可以通过将reverse参数设置为True来实现上面的功能
Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数!
例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写:
def f(): print ‘call f()...‘ # 定义函数g: def g(): print ‘call g()...‘ # 返回函数g: return g
仔细观察上面的函数定义,我们在函数 f 内部又定义了一个函数 g。由于函数 g 也是一个对象,函数名 g 就是指向函数 g 的变量,所以,最外层函数 f 可以返回变量 g,也就是函数 g 本身。
调用函数 f,我们会得到 f 返回的一个函数:
>>> x = f() # 调用f() call f()... >>> x # 变量x是f()返回的函数: <function g at 0x1037bf320> >>> x() # x指向函数,因此可以调用 call g()... # 调用x()就是执行g()函数定义的代码
请注意区分返回函数和返回值:
def myabs(): return abs # 返回函数 def myabs2(x): return abs(x) # 返回函数调用的结果,返回值是一个数值
返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数:
def calc_sum(lst): return sum(lst)
调用calc_sum()函数时,将立刻计算并得到结果:
任务
请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。
#python2.7
def calc_prod(lst):
def f():
return reduce(lambda x,y:x*y,lst)
return f
f = calc_prod([1,2,3,4])
print f()
在函数内部定义的函数和外部定义的函数是一样的,只是他们无法被外部访问:
def g(): print ‘g()...‘ def f(): print ‘f()...‘ return g
将 g 的定义移入函数 f 内部,防止其他代码调用 g:
def f(): print ‘f()...‘ def g(): print ‘g()...‘ return g
但是,考察上一小节定义的 calc_sum 函数:
def calc_sum(lst): def lazy_sum(): return sum(lst) return lazy_sum
注意: 发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 lst。
像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9(请自己动手验证)。
原因就是当count()函数返回了3个函数时,这3个函数所引用的变量 i 的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算 i*i,当 f1 被调用时:
>>> f1()
9 # 因为f1现在才计算i*i,但现在i的值已经变为3
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
任务
返回闭包不能引用循环变量,请改写count()函数,让它正确返回能计算1x1、2x2、3x3的函数。
#python 2.7
def count():
fs =[]
for i in range(1,4):
def f(m=i):
return m*m
fs.append(f)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
利用匿名函数简化以下代码:
def is_not_empty(s): return s and len(s.strip()) > 0 filter(is_not_empty, [‘test‘, None, ‘‘, ‘str‘, ‘ ‘, ‘END‘])
#python 2.7
print filter(lambda s: s and len(s.strip())>0,[‘test‘,None,‘‘,‘str‘,‘ ‘,‘END‘])
return x*x*x
return x*2
def fn(x):
print ‘call‘ + f.__name__+‘()‘
任务
请编写一个@performance,它可以打印出函数调用的时间。
#python 2.7
import time
def performance(f):
def fn(*args,**kw):
print‘call ‘+ f.__name__ +‘() in ‘+ time.strftime(‘%Y-%m-%d %X‘, time.localtime())
return f(*args,**kw)
return fn
@performance
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)
任务
上一节的@performance只能打印秒,请给 @performace 增加一个参数,允许传入‘s‘或‘ms‘:
@performance(‘ms‘) def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1))
#python 2.7
import time
def performance(prefix):
def performance1(f):
def fn(*args,**kw):
start = time.clock()
time.sleep(1)
ret = f(*args,**kw)
end = time.clock()
if prefix ==‘s‘:
print(‘call ‘+f.__name__+"() used ",(end-start),‘s‘)
else:
print(‘call ‘+f.__name__+"() used ",(end-start)*1000,‘ms‘)
return ret
return fn
return performance1
@performance(‘ms‘)
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print(factorial(10))
任务
请思考带参数的@decorator,@functools.wraps应该放置在哪:
def performance(unit): def perf_decorator(f): def wrapper(*args, **kw): ??? return wrapper return perf_decorator
import time, functools
def performance(prefix):
def performance1(f):
@functools.wraps(f)
def fn(*args,**kw):
start = time.clock()
time.sleep(1)
ret = f(*args,**kw)
end = time.clock()
if prefix ==‘s‘:
print(‘call ‘+f.__name__+"() used ",(end-start),‘s‘)
else:
print(‘call ‘+f.__name__+"() used ",(end-start)*1000,‘ms‘)
return ret
return fn
return performance1
@performance(‘ms‘)
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print(factorial.__name__)
任务
在第7节中,我们在sorted这个高阶函数中传入自定义排序函数就可以实现忽略大小写排序。请用functools.partial把这个复杂调用变成一个简单的函数:
sorted_ignore_case(iterable)
import functools
def f(x):
return x.lower()
sorted_ignore_case = functools.partial(sorted,key=f)
print(sorted_ignore_case([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘]))
以上是关于python中的函数式编程与装饰器的主要内容,如果未能解决你的问题,请参考以下文章
Go的魅力, 函数式(柯里化, 闭包, 高阶函数), Python@装饰器, 封装