详解Python 函数式复合和柯里化

  • Post category:Python

Python 函数式编程初探

在 Python 中,函数是「一等公民」(first-class citizen),这意味着函数和其他数据类型(比如字符串和数字)一样重要。

Python 支持多种高级函数特性,例如:函数可以作为参数传递给其他函数,函数可以返回另一个函数等。这些特性允许我们使用函数式编程(functional programming)的技巧来写出优雅的 Python 代码。

函数式复合

函数式复合是函数式编程里的一种常见技巧。简单来说,它是将多个小的函数组合起来形成一个更大的函数,使得代码更加模块化和可读。

具体而言,函数式复合指的是将一个函数作为另一个函数的输入,并返回一个新的函数,新的函数会按照一定的顺序执行多个函数,并将结果传递给下一个函数。在这个过程中,每个函数都是可以独立测试和重用的独立模块。

下面是一个简单的例子,通过函数式复合将字符串中的所有单词首字母大写。

def capitalize_first(s):
    return s[0].upper() + s[1:]

def title_case(s):
    return ' '.join([capitalize_first(w) for w in s.split()])

s = 'hello world'
print(title_case(s))  # Hello World

这种实现方式较为冗长,并且每个单词的首字母大写化和单词划分两个过程耦合在了一起。

我们可以使用函数式复合重新实现该函数:

def compose(f, g):
    return lambda x: f(g(x))

title_case = compose(lambda s: ' '.join(s), compose(lambda s: capitalize_first(s), lambda s: s.split()))
s = 'hello world'
print(title_case(s))  # Hello World

compose 函数接收两个函数 fg,并返回一个新函数 lambda x: f(g(x))title_case 函数使用了这个新函数,将字符串先通过 split 函数划分成单词,再对每个单词使用 capitalize_first 函数进行首字母大写化,最后将单词通过 join 函数重新组合成字符串。

上述代码中的 compose 函数只适用于组合两个函数。如果要组合更多的函数,可以使用下面的代码:

def compose(*functions):
    return lambda x: reduce(lambda acc, f: f(acc), functions, x)

*functions 表示接收任意个函数,将这些函数收集到一个元组中,使用 reduce 函数对这些函数进行累加并组合。

柯里化

柯里化(currying)是指将一个函数拆分成多个接收单一参数的函数,并将每个函数的返回值作为下一个函数的输入,最后返回一个函数处理结果。

柯里化的好处是可以将函数的行为更加充分地明确展现,也能产生更加简洁、可读和可复用的代码。

下面是一个简单的例子,使用柯里化实现两个数字相加。

def add(a, b):
    return a + b

def curried_add(a):
    return lambda b: add(a, b)

add_five = curried_add(5)
print(add_five(3))  # 8

curried_add 函数接收一个参数 a,并返回一个新的函数,该函数接收一个参数 b,调用 add(a, b) 来实现相加操作。最后将 5 作为参数传给 curried_add 函数,生成了一个新的函数,对该函数使用 3 进行操作,输出结果为 8

使用柯里化的好处之一是可以通过与其他函数式编程技术结合使用来生成更加简单、可读和可扩展的代码。

下面是一个使用柯里化和函数式复合结合生成 SQL 查询字符串的例子:

def query(table):
    def select(*fields):
        def where(predicate):
            field_str = ', '.join(fields)
            return f"SELECT {field_str} FROM {table} WHERE {predicate}"
        return where
    return select

sql = query('books')('title', 'author')(lambda book: book['year'] > 1900)
print(sql)  # SELECT title, author FROM books WHERE <function <lambda> at 0x...>

query 函数接收表名作为参数并返回一个新的函数 select,该函数接收查询的列名列表并返回另一个新的函数 where,该函数接收一个谓词函数作为参数并返回 SQL 查询字符串。在上述代码中,最后使用柯里化将表名、列名和谓词分别传递给 queryselectwhere 函数,生成最终的 SQL 查询字符串。

总的来说,Python 的函数式编程技术使得我们可以编写可读性更强、抽象层次更高和更易于重用和扩展的代码。感谢您的提问!