函数式编程

  • 函数本身可以赋值给变量,赋值后变量为函数
  • 允许将函数本身作为参数传入另一个函数
  • 允许返回一个函数

map()函数

接收两个参数,一个是函数,一个是Iterable。

map(func, *iterables)

将传入的函数变量func依次作用到序列的每个元素,并把结果作为新的Iterator返回。

实例1:计算一个list=[1,2,3,4,5,6,7,8,9]每个元素的平方。

使用for循环

# 使用for循环
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def f(n):
    return n * n

L = []
for num in list1:
    L.append(f(num))

print(L)  # [1, 4, 9, 16, 25, 36, 49, 64, 81]

使用map函数

list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def f(n):
    return n * n

# 使用map函数
L2 = map(f, list1)
print(L2)  # <map object at 0x00000270570D6F10>
print(list(L2))  # [1, 4, 9, 16, 25, 36, 49, 64, 81]

实例2:把list所有数字转为字符

list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
L3 = map(str, list1)
print(list(L3))  # ['1', '2', '3', '4', '5', '6', '7', '8', '9']

reduce()函数

reduce把一个函数作用在一个序列[x1,x2,x3.....]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。

实例:计算一个序列所有元素的乘积

使用for循环

list1 = [2, 3, 4, 5, 6]

# 使用for循环
result = 1
for num in list1:
    result = result * num

print(result)   # 720

使用reduce函数

from functools import *

list1 = [2, 3, 4, 5, 6]

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

# 使用reduce函数
res = reduce(f, list1)
print(res)  # 720

使用reduce+匿名函数

from functools import *

list1 = [2, 3, 4, 5, 6]

#使用匿名函数+reduce函数
res2 = reduce(lambda x, y: x*y, list1)
print(res2)

filter()函数

用于过滤序列。参数也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

实例:筛选一个序列中的所有偶数

# 筛选一个序列中的所有偶数
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def f(n):
    return n % 2 == 0

res = filter(f,list1)
print(list(res))    # [2, 4, 6, 8, 10]

sorted()函数

 对所有可迭代的对象进行排序操作。

语法

sorted(iterable, key=None, reverse=False)
  • iterable -- 可迭代对象。
  • key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。

实例1:对一个序列进行默认排序和反向排序

L = [19, 2, 34, 5, 6, 12, 8]
print("L进行sorted默认排序的结果:", sorted(L))
print("L进行sorted升序排序的结果:", sorted(L, reverse=True))

实例2:接收一个key来实现自动排序。忽略大小写,按照字母顺序排序

# 接收一个key来实现自动排序。忽略大小写,按照字母顺序排序
L1 = ['bob', 'about', 'Zoo', 'Credit']
L1_tm = sorted(L1, key=str.lower)
print(L1_tm)    # ['about', 'bob', 'Credit', 'Zoo']

实例3:用一组tuple表示学生名字和成绩,分别按照名字和成绩进行排序

L2 = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]

L2 = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]


# 按名字排序
def by_name(n):
    return n[0].lower()


L2_1 = sorted(L2, key=by_name)  # 也可以使用匿名函数
print("按名字排序", L2_1)    # 按名字排序 [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]

# 按成绩升序排序
L2_2 = sorted(L2, key=lambda s: s[1])
print("按成绩升序排序:", L2_2)       # 按成绩升序排序: [('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)]

# 按成绩降序排序
L2_2 = sorted(L2, key=lambda s: s[1], reverse=True)
print("按成绩降序排序:", L2_2)       # 按成绩降序排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]

返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

实例:实现一个可变参数的求和。通常情况下,求和的函数是这样定义的。

def sumCal(*args):
    s = 0
    for n in args:
        s = s + n
    return s


f = sumCal(*[1, 2, 3, 4, 5, 6])
print(f)  # 21

如果我们不需要立即计算,可以不返回求和的结果,而是返回求和的函数

def lazy_sum(*args):
    def sumCal():
        s = 0
        for n in args:
            s = s + n
        return s

    return sumCal


f1 = lazy_sum(1, 2, 3, 4, 5, 6)
print(f1)  # <function lazy_sum.<locals>.sumCal at 0x000001CE59C36280>
print(f1())  # 21

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数,调用函数f1()时,返回的才是真正的求和结果。

我们在函数lazy_sum()中又定义了函数sumCal,并且,内部函数sumCal可以引用外部函数lazy_sum()的参数和局部变量,当lazy_sum()返回函数sumCal时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。

当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。

def lazy_sum(*args):
    def sumCal():
        s = 0
        for n in args:
            s = s + n
        return s

    return sumCal


f1 = lazy_sum(1, 2, 3, 4, 5, 6)
f2 = lazy_sum(1, 2, 3, 4, 5, 6)
print(f1 == f2)  # false

 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

匿名函数

使用 lambda 来创建匿名函数。不再使用 def 语句这样标准的形式定义一个函数。

  • lambda 只是一个表达式,函数体比 def 简单很多。
  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
  • 匿名函数不用写return,返回值就是该表达式的结果。
  • 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。
  • 也可以把匿名函数作为返回值返回。
f = lambda n: n * n
print(f(5))  # 25

# 匿名函数作为返回值
def res(x, y):
    return lambda: x * x + y * y


f1 = res(2, 3)
print(f1)  # <function res.<locals>.<lambda> at 0x0000025958646310>
print(f1())  # 13

偏函数

functools.partial可以帮助我们创建一个偏函数。作用是把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

实例:当我们需要把一个字符串转换为整数时,可以使用int函数。

st = "1234567"
print("st=", st, "st的类型:", type(st))  # st= 1234567 st的类型: <class 'str'>
st1 = int(st)  # 当仅传入字符串时,默认按照十进制转换
print("st1=", st1, "st1的类型:", type(st1))  # st1= 1234567 st1的类型: <class 'int'>
st2 = int(st, base=8)  # 加base参数,按照8进制转换
print("st2=", st2, "st2的类型:", type(st1))  # st2= 342391 st2的类型: <class 'int'>

当需要进行大量的进制计算时,可以定义一个函数来转换

def int2(x, base=2):
    return int(x, base)


print(int2("100100"))  # 36
print(int2("10010"))  # 18

使用偏函数

int3 = functools.partial(int, base=2)
print(int3("100100"))   # 36

int3仅仅只是在int函数中把参数设置为默认参数2,但也可以在函数调用时传入其他值。

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。