[Python进阶-1]高阶函数:闭包/装饰器/functools/lambda/map/filter

时间:2021-07-11 20:03:03

慕课网视频总结:

(1)高阶函数:就是函数可以作为变量,传给另一个函数使用。如:

import math
def add(x, y, f):
return f(x) + f(y)
print add(25, 9, math.sqrt)

(2)map函数,第一个参数是函数,第二个参数是list,函数可以作用list中的每一个元素,并返回一个新的list,原list没有改变。如:

def format_name(s):
x1=s[0]
x2=s[1:]
return x1.upper()+x2.lower()
print map(format_name, ['adam', 'LISA', 'barT'])

(3)reducce函数,至少接受2个参数,第一个是函数,第二个是list,第三个是初始值,可省略。reduce是先拿list的前2个元素做计算,得到的结果再和第3个计算,结果再和第4个……这样反复计算。如果有初始值,则初始值先和第一个计算,结果再和第2个计算……,如求积:

def prod(x, y):
return x*y

print reduce(prod, [2, 4, 5, 7, 12])

(4)filter函数,过滤不满足条件的元素,返回符合条件的元素list,如:

import math

def is_sqr(x):
r=int(math.sqrt(x))
return r*r==x
print filter(is_sqr, range(1, 101))

(5)sorted排序,默认升序。

def cmp_ignore_case(s1, s2):
if s1.lower()<s2.lower():
return -1
if s1.lower()>s2.lower():
return 1
return 0

print sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)

(6)返回函数

def calc_prod(lst):
def prod():
s=1
for x in lst:
s=s*x
return s
return prod

f = calc_prod([1, 2, 3, 4])
print f()

(7)闭包。如(6)所示,内层函数引用了外层函数的局部变量,并且返回内层函数的情况,叫闭包。闭包需要注意的是,引用的局部变量最好在返回内层函数后不要改变,因为这个时候只要内层函数没有使用括号()来调用,他们里面保存的都不是具体的值,而是包含局部变量的计算公式等。所以如果改变了局部变量,那么最终结果会有变化,所以,闭包情况下,for循环里面的i就不使用作为局部变量给内层函数进行计算。因为i最终变成了最后的那个值。

def count():
fs = []
for i in range(1, 4):
def f(j):
def g():
return j*j
return g
r=f(i)
fs.append(r)
return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

(8)lambda简写函数

def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])

变成:

print filter(lambda s:s and len(s.strip())>0, ['test', None, '', 'str', '  ', 'END'])

(9)装饰器,decorator。如果一个函数被调用时需要增加一些功能,要么重写这个函数,要么利用高阶函数传入这个函数然后在高阶函数内部改造最后返回一个新的函数。后一种方法,即装饰器。装饰器可以先单独写一个改造函数的函数,然后在需要被改造的函数前面使用@装饰器名字,即改造函数的函数名。即,做了分离,利于重复使用这个装饰器。

import time

def performance(f):
def fn(*args,**kw):
t1=time.time()
r=f(*args,**kw)
t2=time.time()
print 'call %s() in %f'%(f.__name__,t2-t1)
return r
return fn

@performance
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)


(10)可以传递参数的装饰器,就是再外包一层。

import time

def performance(unit):
def deco(f):
def wrapper(*args,**kw):
t1=time.time()
r=f(*args,**kw)
t2=time.time()
t=(t2-t1)*1000 if unit=='ms' else (t2-t1)
print 'call %s() in %f %s'%(f.__name__,t,unit)
return r
return wrapper
return deco

@performance('ms')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

(11)@functools.wraps是把一个函数的属性复制给另一个函数,比如__name__,__doc__属性。在这里,经过装饰器后,原函数的属性也会被改变成在装饰器里定义的函数的属性,即factorial函数属性被更改成了wrapper函数的属性。如果要保留原有属性,就需要在wrapper中把原属性复制给新函数的属性,如wrapper.__name__=f.__name__等,但属性太多,一个个复制不现实,所以有functools.wraps,自动复制。也是类似于装饰器用@,即在新函数代码的上面进行设置,此处,即在def wrapper(*args,**kw) 的上面定义。

import time, functools

def performance(unit):
def deco(f):
@functools.wraps(f)
def wrapper(*args,**kw):
print 'call'
t1=time.time()
r=f(*args,**kw)
t2=time.time()
t=(t2-t1)*1000 if unit=='ms' else (t2-t1)
print 'call %s in %f'%(f.__name__,t)
return r
return wrapper
return deco

@performance('ms')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))

print factorial.__name__

(12)functools里的partial可以定义偏函数,实质上还是改造一个函数。但这里的改造重点是在参数上的改造。如int(s,base=8),把一个字符串改成8进制的整数,这里就可以定一个新的函数int8=...把base=8作为默认参数,这样以后直接实用化int8函数即可。下面是一个不区分大小写的排序。也是对sorted进行了改造。

import functools

sorted_ignore_case = functools.partial(sorted,cmp=lambda s1,s2 : cmp(s1.upper(),s2.upper()))

print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])

函数这一块,最核心的概念是闭包/装饰器。其实说白了就是高阶函数的应用。而高阶函数,其实就是函数套函数的概念。