偏函数, 高阶函数, 返回函数, 匿名函数, 闭包

偏函数
  • 当我们写一个参数比较多的函数时, 如果有些参数, 大部分场景下都是某一个固定值, 那么为了简化使用, 就可以创建一个新函数, 指定我们要使用的函数的某个参数为某个固定的值; 这个新函数就是"偏函数"
  • 即: 在原有函数的基础上, 写一个新函数, 来简化这个函数的使用
  • 例如: 此时有一个函数, 拥有四个参数
def aFunc(a, b, c, d=1):
    print(a + b + c + d)
复制代码
  • 此时, 我们需要在大量地方使用这个函数, 但是d的值为2, 如果每次都要指定d, 是很麻烦的事, 所以我们再次创建一个函数
  • 方式一: 写一个新函数, 指定d=2, 如下, 这样我们只需要调用bFunc函数就可以了
def bFunc(a, b, c, d=2):
    aFunc(a, b, c, d)
bFunc(1, 2, 3)      # 打印: 8
复制代码
  • 方式二: 借助functools模块的partial函数
bFunc = functools.partial(aFunc, d = 2)
bFunc(1, 2, 3)      # 打印: 8
复制代码
  • 偏函数的一个应用场景
import functools
int2 = functools.partial(int, base=16)
result = int2("15")
print(result)       # 打印: 21, 认为15是十六进制数, 转为十进制就是21
复制代码
高阶函数
  • 当一个函数A的参数, 接收的又是另一个函数时, 则把这个函数A称为是"高阶函数"
  • 例如排序函数sorted
temp = [{"name": "张三", "age" : 18}, {"name" : "李四", "age" : 20}, {"name" : "王五", "age" : 19}]
def value(x):
    return x["age"]
result = sorted(temp, key=value)
print(result)
# 打印: [{'name': '张三', 'age': 18}, {'name': '王五', 'age': 19}, {'name': '李四', 'age': 20}]
复制代码
  • 上述代码中, sorted函数的参数key接收的是另一个参数temp, 所以sorted就是高阶函数
返回函数
  • 一个函数A的返回值是一个函数B, 那么这种操作被称为返回函数
def aFunc(symbol):
    def add(num1, num2):
        print(num1 + num2)
    def subtract(num1, num2):
        print(num1 - num2)

    if symbol == "+":
        return add
    if symbol == "-":
        return subtract

aFunc("+")(1, 2)        # 打印: 3
aFunc("-")(5, 3)        # 打印: 2
复制代码
  • 上述aFunc函数, 根据传入的参数不同, 返回不同的函数, 进行不同的操作
匿名函数
  • 即没有名字的函数, 也称为 "lambda函数"
  • 表达式:
lambda 参数列表: 表达式
复制代码
  • 限制
  • 只能写一个表达式, 且不能return
  • 表达式的结果就是返回值
  • 只适用于一些简单的操作
  • 示例:
func = lambda x, y : x + y
result = func(1, 2)
print(result)       # 打印: 3
复制代码
闭包
  • 概念:
  • 在嵌套函数的前提下
  • 内层函数引用了外层函数的变量(包括参数)
  • 外层函数, 又把内层函数 当做返回值进行返回
def test1():
    a = 10
    def test2():
        print(a)
    return test2
newFunc = test1()
newFunc()           # 打印 10
复制代码
  • 一个简单的应用场景: 绘制分割线
def line_config(content, num):
    print("-" * (num // 2) + content + "-" * (num // 2))
print("呵呵", 20)       # 打印: ----------呵呵----------
复制代码
  • 此时每次调用都需要使用代码
print("呵呵", 20)       # 打印: ----------呵呵----------
复制代码
  • 如果每次的contentnum不同, 那么无可厚非, 但是如果每次调用时参数一致, 那么就显得比较麻烦, 此时就可以使用闭包形式对上面代码进行改动
def line_config(content, num):
 def line():
        print("-" * (num // 2) + content + "-" * (num // 2))
 return line
newFunc = line_config("呵呵", 20)
newFunc()             # 打印: ----------呵呵----------
newFunc()             # 打印: ----------呵呵----------
newFunc()             # 打印: ----------呵呵----------
newFunc()             # 打印: ----------呵呵----------
复制代码
  • 此时如果需要新的函数, 可以如下使用
newFunc2 = line_config("哈哈", 20)
newFunc2()             # 打印: ----------哈哈----------
newFunc2()             # 打印: ----------哈哈----------
newFunc2()             # 打印: ----------哈哈----------
newFunc2()             # 打印: ----------哈哈----------
复制代码
  • 此时每次调用只需使用newFunc2函数
  • 注意事项一: 闭包中内部函数修改外部函数中的变量(包括参数)
  • 需要使用关键字 nonlocal 声明需要被修改的变量, 否则会默认为内部函数新定义的变量, 与外界变量无关
def test():
  num = 10
  def test2():
      nonlocal num
      num = 666
    print(num)
    test2()
    print(num)
    return test2
test()() 
复制代码
  • 上诉代码中的打印结果为
10
666
复制代码
  • 如果注销掉 nonlocal num, 那么最后打印的结果是
10
10
复制代码
  • 注意事项二: 函数, 是被调用时, 才去确定变量标识所对应的值
def test1():
    funcs = []
    for i in range(0, 3):
        def test2():
            print(i)
        funcs.append(test2)
    return funcs

arr = test1()
print(arr)

arr[0]()
arr[1]()
arr[2]()
复制代码
  • 打印结果如下:
[<function test1.<locals>.test2 at 0x1044227b8>, <function test1.<locals>.test2 at 0x104422840>, <function test1.<locals>.test2 at 0x1044228c8>]
2
2
2
复制代码
  • 列表中三个函数的地址不同, 说明是是三个不同的函数, 此时分别取出调用, 发现结构都是 2
  • 这说明在执行test1()时, 内部的test2函数没有被调用过
  • 只有在从列表中取出时才调用, 此时i的值已经循环到最大的2
  • 此时才确定i的值, 所以打印都是2
  • 如果想打印出0, 1, 2, 可以使用下面的代码
def test():
    funcs = []
    for i in range(0, 3):
        def test2(num):
            def test1():
                print(num)
            return test1
        funcs.append(test2(i))
    return funcs

newFuncs = test()

print(newFuncs)

newFuncs[0]()           # 0
newFuncs[1]()           # 1
newFuncs[2]()           # 2
复制代码