python函数式编程_Python函数式编程简介

python函数式编程

Most of us have been introduced to Python as an object-oriented language; a language exclusively using classes to build our programs. While classes, and objects, are easy to start working with, there are other ways to write your Python code. Languages like Java can make it hard to move away from object-oriented thinking, but Python makes it easy.

我们大多数人都已经将Python作为一种面向对象的语言进行了介绍。 一种仅使用类来构建程序的语言。 虽然类和对象很容易开始使用,但是还有其他方式可以编写Python代码。 诸如Java之类的语言很难摆脱面向对象的思维,但是Python使其变得容易。

Given that Python facilitates different approaches to writing code, a logical follow-up question is: what is a different way to write code? While there are several answers to this question, the most common alternative style of writing code is called functional programming. Functional programming gets its name from writing functions which provides the main source of logic in a program.

鉴于Python促进了不同的代码编写方法,因此逻辑上的后续问题是:什么是不同的代码编写方式? 尽管对该问题有多个答案,但是最常见的替代代码编写方式称为函数式编程 。 函数式编程的名称源于编写函数的功能,这些函数提供了程序中逻辑的主要来源。

In this post, we will:* Explain the basics of functional programming by comparing it to object-oriented programming.* Cover why you might want to incorporate functional programming in your own code.* Show you how Python allows you to switch between the two.

在这篇文章中,我们将:*通过将函数式编程与面向对象的编程进行比较来说明函数式编程的基础。*讨论为什么要将函数式编程合并到自己的代码中。*向您展示Python如何允许您在两者之间进行切换。

比较面向对象和功能 (Comparing object-oriented to functional)

The easiest way to introduce functional programming is to compare it to something we’re already aware of: object-oriented programming. Suppose we wanted to create a line counter class that takes in a file, reads each line, then counts the total amount of lines in the file. Using a class, it could look something like the following:

引入函数式编程的最简单方法是将其与我们已经意识到的东西进行比较:面向对象的编程。 假设我们想创建一个行计数器类,该类接收一个文件,读取每一行,然后计算文件中的总行数。 使用一个类,它可能类似于以下内容:

class LineCounter:
    def __init__(self, filename):
        self.file = open(filename, 'r')
        self.lines = []
    
    def read(self):
        self.lines = [line for line in self.file]
    
    def count(self):
        return len(self.lines)

class LineCounter:
    def __init__(self, filename):
        self.file = open(filename, 'r')
        self.lines = []
    
    def read(self):
        self.lines = [line for line in self.file]
    
    def count(self):
        return len(self.lines)

While not the best implementation, it does provide an insight into object-oriented design. Within the class, there are the familiar concepts of methods and properties. The properties set and retrieve the state of the object, and the methods manipulate that state.

虽然不是最佳实现,但它确实提供了对面向对象设计的了解。 在类中,有一些熟悉的方法和属性的概念。 这些属性设置并检索对象的状态,然后这些方法操纵该状态。

For both these concepts to work, the object’s state must change over time. This change of state is evident in the lines property after calling the read() method. As an example, here’s how we would use this class:

为了使这两个概念同时起作用,对象的状态必须随时间变化。 调用read()方法后,状态变化在lines属性中显而易见。 例如,这是我们使用此类的方法:

The ever-changing state of an object is both its blessing and curse. To understand why a changing state can be seen as a negative, we have to introduce an alternative. The alternative is to build the line counter as a series of independent functions.

物体不断变化的状态既是物体的祝福也是诅咒。 为了理解为什么将变化的状态视为负面状态,我们必须引入一种替代方法。 另一种方法是将线路计数器构建为一系列独立的功能。

def read(filename):
    with open(filename, 'r') as f:
        return [line for line in f]

def count(lines):
    return len(lines)

example_lines = read('example_log.txt')
lines_count = count(example_lines)

def read(filename):
    with open(filename, 'r') as f:
        return [line for line in f]

def count(lines):
    return len(lines)

example_lines = read('example_log.txt')
lines_count = count(example_lines)

使用纯函数 (Working with pure functions)

In the previous example, we were able to count the lines only with the use of functions. When we only use functions, we are applying a functional approach to programming which is, non-excitingly, called functional programming. The concepts behind functional programming requires functions to be stateless, and rely only on their given inputs to produce an output.

在前面的示例中,我们只能使用函数来计算行数。 当我们仅使用函数时,我们将一种函数方法应用于编程,这很自然地称为函数编程 。 函数式编程背后的概念要求函数是无状态的 ,并且仅依赖于给定的输入来产生输出。

The functions that meet the above criteria are called pure functions. Here’s an example to highlight the difference between pure functions, and non-pure:

满足以上条件的函数称为纯函数 。 这是一个突出纯函数与非纯函数之间差异的示例:

The benefit of using pure functions over impure (non-pure) functions is the reduction of side effects. Side effects occur when there are changes performed within a function’s operation that are outside its scope. For example, they occur when we change the state of an object, perform any I/O operation, or even call print():

使用纯函数优于不纯函数(非纯函数)的好处是减少了副作用 。 如果在函数的操作范围内执行了超出其范围的更改,则会发生副作用。 例如,当我们更改对象的状态,执行任何I / O操作甚至调用print()时,它们就会发生:

def read_and_print(filename):
    with open(filename) as f:
        # Side effect of opening a
        # file outside of function.
        data = [line for line in f]
    for line in data:
        # Call out to the operating system
        # "println" method (side effect).
        print(line)

def read_and_print(filename):
    with open(filename) as f:
        # Side effect of opening a
        # file outside of function.
        data = [line for line in f]
    for line in data:
        # Call out to the operating system
        # "println" method (side effect).
        print(line)

Programmers reduce side effects in their code to make it easier to follow, test, and debug. The more side effects a codebase has, the harder it is to step through a program and understand its sequence of execution.

程序员减少了代码中的副作用,使其更易于跟踪,测试和调试。 代码库的副作用越多,遍历程序并理解其执行顺序就越困难。

While it’s convienent to try and eliminate all side effects, they’re often used to make programming easier. If we were to ban all side effects, then you wouldn’t be able to read in a file, call print, or even assign a variable within a function. Advocates for functional programming understand this tradeoff, and try to eliminate side effects where possible without sacrificing development implementation time.

尝试消除所有副作用是很方便的,但它们通常用于简化编程。 如果我们要禁止所有副作用,那么您将无法读取文件,调用print甚至无法在函数中分配变量。 函数式编程的倡导者理解这种折衷,并在不牺牲开发实现时间的情况下,尽可能消除副作用。

Lambda表达 (The Lambda Expression)

Instead of the def syntax for function declaration, we can use a lambda expression to write Python functions. The lambda syntax closely follows the def syntax, but it’s not a 1-to-1 mapping. Here’s an example of building a function that adds two integers:

可以使用lambda表达式代替Python声明函数的def语法。 lambda语法紧随def语法,但不是def映射。 这是构建一个将两个整数相加的函数的示例:

The lambda expression takes in a comma seperated sequences of inputs (like def). Then, immediately following the colon, it returns the expression without using an explicit return statement. Finally, when assigning the lambda expression to a variable, it acts exactly like a Python function, and can be called using the the function call syntax: new_add().

lambda表达式采用逗号分隔的输入序列(例如def )。 然后,紧接在冒号之后,它不使用显式的return语句而返回表达式。 最后,在将lambda表达式分配给变量时,它的行为完全类似于Python函数,可以使用以下函数调用语法进行调用: new_add()

If we didn’t assign lambda to a variable name, it would be called an anonymous function. These anonymous functions are extremely helpful, especially when using them as an input for another function. For example, the sorted() function takes in an optional key argument (a function) that describes how the items in a list should be sorted.

如果我们不将lambda分配给变量名,则将其称为匿名函数 。 这些匿名函数非常有用,尤其是在将它们用作另一个函数的输入时。 例如, sorted() 函数接受一个可选的key参数(一个函数),该参数描述应如何对列表中的项目进行排序。

unsorted = [('b', 6), ('a', 10), ('d', 0), ('c', 4)]

# Sort on the second tuple value (the integer).
print(sorted(unsorted, lambda x: x[1]))
>> [('d', 0), ('c', 4), ('b', 6), ('a', 10)]

unsorted = [('b', 6), ('a', 10), ('d', 0), ('c', 4)]

# Sort on the second tuple value (the integer).
print(sorted(unsorted, lambda x: x[1]))
>> [('d', 0), ('c', 4), ('b', 6), ('a', 10)]

地图功能 (The Map Function)

While the ability to pass in functions as arguments is not unique to Python, it is a recent development in programming languages. Functions that allow for this type of behavior are called first-class functions. Any language that contains first-class functions can be written in a functional style.

尽管将函数作为参数传递的能力并不是Python独有的,但这是编程语言的最新发展。 允许这种行为的函数称为一等函数 。 包含一流功能的任何语言都可以以功能样式编写。

There are a set of important first-class functions that are commonly used within the functional paradigm. These functions take in a Python iterable, and, like sorted(), apply a function for each element in the list. Over the next few sections, we will examine each of these functions, but they all follow the general form of function_name(function_to_apply, iterable_of_elements).

有一组重要的一流功能通常在功能范式中使用。 这些函数采用Python可迭代 ,并且像sorted() ,为列表中的每个元素应用一个函数。 在接下来的几节中,我们将研究每个函数,但是它们都遵循function_name(function_to_apply, iterable_of_elements)的一般形式。

The first function we’ll work with is the map() function. The map() function takes in an iterable (ie. list), and creates a new iterable object, a special map object. The new object has the first-class function applied to every element.

我们将使用的第一个函数是map()函数。 map()函数接受一个可迭代的(即list ),并创建一个新的可迭代对象,即特殊的map对象。 新对象将一流的功能应用于每个元素。

Here’s how we could use map to add 10 or 20 to every element in a list:

这是我们可以使用map向列表中的每个元素添加1020的方法:

values = [1, 2, 3, 4, 5]

# Note: We convert the returned map object to
# a list data structure.
add_10 = list(map(lambda x: x + 10, values))
add_20 = list(map(lambda x: x + 20, values))

print(add_10)
>> [11, 12, 13, 14, 15]

print(add_20)
>> [21, 22, 23, 24, 25]

values = [1, 2, 3, 4, 5]

# Note: We convert the returned map object to
# a list data structure.
add_10 = list(map(lambda x: x + 10, values))
add_20 = list(map(lambda x: x + 20, values))

print(add_10)
>> [11, 12, 13, 14, 15]

print(add_20)
>> [21, 22, 23, 24, 25]

Note that it’s important to cast the return value from map() as a list object. Using the returned map object is difficult to work with if you’re expecting it to function like a list. First, printing it does not show each of its items, and secondly, you can only iterate over it once.

请注意,将map()的返回值转换为list对象很重要。 如果期望返回的map对象像list一样起作用,则很难使用它。 首先,打印它不会显示每个项目,其次,您只能对其进行一次迭代。

过滤功能 (The Filter Function)

The second function we’ll work with is the filter() function. The filter() function takes in an iterable, creates a new iterable object (again, a special map object), and a first-class function that must return a bool value. The new map object is a filtered iterable of all elements that returned True.

我们将使用的第二个函数是filter()函数。 filter()函数接受一个I​​terable,创建一个新的Iterable对象(再次是一个特殊的map对象),以及一个必须返回bool值的一流函数。 新的map对象是返回True的所有元素的过滤迭代。

Here’s how we could filter odd or even values from a list:

这是我们如何从列表中过滤奇数或偶数的方法:

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

# Note: We convert the returned filter object to
# a list data structure.
even = list(filter(lambda x: x % 2 == 0, values))
odd = list(filter(lambda x: x % 2 == 1, values))

print(even)
>> [2, 4, 6, 8, 10]

print(odd)
>> [1, 3, 5, 7, 9]

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

# Note: We convert the returned filter object to
# a list data structure.
even = list(filter(lambda x: x % 2 == 0, values))
odd = list(filter(lambda x: x % 2 == 1, values))

print(even)
>> [2, 4, 6, 8, 10]

print(odd)
>> [1, 3, 5, 7, 9]

约简功能 (The Reduce Function)

The last function we’ll look at is the reduce() function from the functools package. The reduce() function takes in an iterable, and then reduces the iterable to a single value. Reduce is different from filter() and map(), because reduce() takes in a function that has two input values.

我们要看的最后一个函数是functools包中reduce()函数。 reduce()函数接受一个I​​terable,然后将Iterable减小为单个值。 Reduce与filter()map()有所不同,因为reduce()接受具有两个输入值的函数。

Here’s an example of how we can use reduce() to sum all elements in a list.

这是一个示例,说明如何使用reduce()对列表中的所有元素求和。

An interesting note to make is that you do not have to operate on the second value in the lambda expression. For example, you can write a function that always returns the first value of an iterable:

一个有趣的注意,使的是,你不必对在第二值操作lambda表达式。 例如,您可以编写一个始终返回可迭代对象的第一个值的函数:

from functools import reduce

values = [1, 2, 3, 4, 5]

# By convention, we add `_` as a placeholder for an input
# we do not use.
first_value = reduce(lambda a, _: a, values)
print(first_value)
>> 1

from functools import reduce

values = [1, 2, 3, 4, 5]

# By convention, we add `_` as a placeholder for an input
# we do not use.
first_value = reduce(lambda a, _: a, values)
print(first_value)
>> 1

用列表理解进行重写 (Rewriting with list comprehensions)

Because we eventually convert to lists, we should rewrite the map() and filter() functions using list comprehension instead. This is the more pythonic way of writing them, as we are taking advantage of the Python syntax for making lists. Here’s how you could translate the previous examples of map() and filter() to list comprehensions:

因为我们最终会转换为列表,所以我们应该改用列表理解来重写map()filter()函数。 这是编写它们的更Python化的方式,因为我们利用Python语法来创建列表。 这是如何翻译map()filter()的先前示例以列出理解的方法:

From the examples, you can see that we don’t need to add the lambda expressions. If you are looking to add map(), or filter() functions to your own code, this is usually the recommended way. However, in the next section, we’ll provide a case to still use the map() and filter() functions.

从示例中,您可以看到我们不需要添加lambda表达式。 如果您希望将map()filter()函数添加到自己的代码中,通常是推荐的方法。 但是,在下一节中,我们将提供一个仍然使用map()filter()函数的情况。

编写功能部分 (Writing Function Partials)

Sometimes we want to use the behavior of a function, but decrease the number of arguments it takes. The purpose is to “save” one of the inputs, and create a new function that defaults the behavior using the saved input. Suppose we wanted to write a function that would always add 2 to any number:

有时我们想使用函数的行为,但是减少了它所需要的参数数量。 目的是“保存”其中一个输入,并创建一个新功能,该功能使用保存的输入来默认行为。 假设我们想编写一个函数,该函数总是将2加到任何数字上:

def add_two(b):
    return 2 + b 

print(add_two(4))
>> 6

def add_two(b):
    return 2 + b 

print(add_two(4))
>> 6

The add_two function is similar to the general function, $f(a,b) = a + b$, only it defaults one of the arguments ($a = 2$). In Python, we can use the partial module from the functools package to set these argument defaults. The partial module takes in a function, and “freezes” any number of args (or kwargs), starting from the first argument, then returns a new function with the default inputs.

add_two函数类似于通用函数$ f(a,b)= a + b $,只是它默认使用其中一个参数($ a = 2 $)。 在Python中,我们可以使用functools包中的partial模块来设置这些参数默认值。 partial模块接收一个函数,并从第一个参数开始“冻结”任意数量的args(或kwarg),然后返回具有默认输入的新函数。

Partials can take in any function, including ones from the standard library.

部分函数可以接受任何函数,包括标准库中的函数。

# A partial that grabs IP addresses using
# the `map` function from the standard library.
extract_ips = partial(
    map,
    lambda x: x.split(' ')[0]
)
lines = read('example_log.txt')
ip_addresses = list(extract_ip(lines))

# A partial that grabs IP addresses using
# the `map` function from the standard library.
extract_ips = partial(
    map,
    lambda x: x.split(' ')[0]
)
lines = read('example_log.txt')
ip_addresses = list(extract_ip(lines))

翻译自: https://www.pybloggers.com/2018/01/introduction-to-functional-programming-in-python/

python函数式编程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值