功能测试代码python
Functional programming has been getting more and more popular in recent years. Not only is it perfectly suited for tasks like data analysis and machine learning. It’s also a powerful way to make code easier to test and maintain.
近年来,函数式编程越来越流行。 它不仅非常适合数据分析和机器学习等任务。 这也是使代码更易于测试和维护的强大方法。
The trend is easy to see: Even though they’re still in a bit of a niche, purely functional languages like Elm and Haskell are gaining traction. Languages that are somewhat functional, like Scala and Rust, are taking off. And popular languages like C++ and Python are adding more and more pieces of functional programming to their repertoire.
趋势很容易看出:尽管它们仍然处于利基市场,但是像Elm和Haskell这样的纯函数式语言正逐渐受到关注。 具有某种功能的语言(例如Scala和Rust )正在兴起。 诸如C ++和Python之类的流行语言正在为其功能表添加越来越多的功能编程。
If you’ve used to object-oriented code, writing functional programs can seem scary at first. The good news is that you can mix-and-match functional and object-oriented code pretty well. A few tweaks from functional programming are often enough to reap some benefits. So let’s get to it!
如果您已经习惯了面向对象的代码,那么一开始编写功能程序似乎很恐怖。 好消息是您可以很好地混合和匹配功能和面向对象的代码。 函数式编程的一些调整通常足以获得一些好处。 因此,让我们开始吧!
纯功能 (Pure functions)
The pesky thing with non-functional programming is that functions can have side effects. That is, they make use of variables that don’t necessarily appear in the declaration of the function.
非函数式编程的烦人之处在于函数可能会产生副作用。 也就是说,它们利用了不一定出现在函数声明中的变量。
Consider this simple example, where we’re adding two numbers:
考虑这个简单的示例,我们在其中添加两个数字:
b = 3
def add_ab(a):
return a + b
add_ab(5)
The global variable b
doesn’t appear in the declaration of add_ab
, so if you want to debug it, you’ll have to check that b
is in fact used. Sounds simple, but it can get tedious with bigger programs. We can fix this easily by being honest about what we’re putting into the function:
全局变量b
不会出现在add_ab
的声明中,因此,如果要调试它,则必须检查b
是否确实已使用。 听起来很简单,但是在大型程序中可能会变得乏味。 我们可以通过诚实地对待函数中所包含的内容来轻松解决此问题:
def add_ab_functional(a, b):
return a + b
add_ab_functional(5, 3)
This is just a dumb little example. But with larger programs, you’ll notice how much easier you can understand and debug the code when you don’t need to worry about side-effects.
这只是一个愚蠢的小例子。 但是对于大型程序,您无需担心副作用,就会注意到可以轻松理解和调试代码。
高阶函数 (Higher-order functions)
In functional programming, you can nest functions: either you design a function that takes another function as an argument, or you code a function that returns another function.
在函数式编程中,您可以嵌套函数:设计一个将另一个函数作为参数的函数,或者编写一个返回另一个函数的函数。
As an example for a function that takes another function, consider that you have an array of numbers and you’d like to calculate the sine, cosine, and exponential of that array. You could, in theory, write it like this (numpy
is a Python package for maths):
作为使用另一个函数的函数的示例,请考虑您有一个数字数组,并且想要计算该数组的正弦,余弦和指数。 从理论上讲,您可以这样写( numpy
是用于数学的Python包):
import numpy as np# make a list of numbers as input values for functions
numbers_list = np.arange(0, 2*np.pi, np.pi/10).tolist()# calculate sinedef do_sin(numbers):
return np.sin(numbers)
sin_list = do_sin(numbers_list)# calculate cosinedef do_cos(numbers):
return np.cos(numbers)
cos_list = do_cos(numbers_list)# calculate exponentialdef do_exp(numbers):
return np.exp(numbers)
exp_list = do_exp(numbers_list)
This is nice and simple, but it’s kind of annoying to write three different functions with the exact same structure. Instead, we can write a function that takes the other functions like so:
这是很好而且很简单,但是用完全相同的结构编写三个不同的函数有点烦人。 相反,我们可以编写一个采用其他功能的函数,如下所示:
import numpy as np# make a list of numbers as input values for functions
numbers_list = np.arange(0, 2*np.pi, np.pi/10).tolist()# calculate with some functiondef do_calc(numbers, function):
return function(numbers)# calculate sin, cos, and exp
new_sin_list = do_calc(numbers_list, np.sin)
new_cos_list = do_calc(numbers_list, np.cos)
new_exp_list = do_calc(numbers_list, np.exp)
Not only is this more concise and nicer to read. It’s also easier to expand because you only need to add one line for a new function
, instead of three in the example above.
这不仅更简洁,而且更易于阅读。 扩展起来也更容易,因为您只需为新function
添加一行,而不是上面示例中的三行。
You can also turn the concept of a function in a function upside down: Not only can you make a function take another function as an argument; you can also make it return an argument.
您也可以颠倒一个函数的概念:不仅可以使一个函数接受另一个函数作为参数,还可以使另一个函数成为参数。 您还可以使其返回参数。
Imagine you have an array of numbers, and you need to increment each element of the array by 2:
假设您有一个数字数组,并且需要将数组的每个元素增加 2:
def add2(numbers):
incremented_nums = []
for n in numbers:
incremented_nums.append(n + 2)
return incremented_numsprint(add2([23, 88])) # returns [25, 90]
If you want to increment the elements of an array by something else, you could, of course, copy-paste this function and replace the 2
by that increment. But there’s a more elegant solution: we can write a function that takes any increment and returns another function that performs what add2
does, but for whichever increment.
如果您想以其他方式增加数组的元素,则可以复制粘贴此函数,并以该增量替换2
。 但是,还有一个更优雅的解决方案:我们可以编写一个函数,该函数可以任意递增,并返回另一个函数,该函数执行add2
所做的事情,但无论递增多少。
def add_num(increment):
def add_inc(numbers):
incremented_nums = []
for n in numbers:
incremented_nums.append(n + increment)
return incremented_nums
return add_incadd5_25 = add_num(5.25)
print(add5_25([23.37,88.93])) # returns [28.62, 94.18]
With this routine, each new function takes one line to define instead of five. And like the function that takes a function, the function returning a function is easier to debug: if you had a bug in add_num
, you’d only have to fix that. You don’t need to go back and fix add2
and whichever other function you might have defined in the same way. The bigger programs get, more this pays off.
使用此例程,每个新函数只需要一行定义,而不是五行。 与采用函数的函数一样,返回函数的函数更易于调试:如果在add_num
存在错误, add_num
只需对其进行修复。 您无需返回并修复add2
以及可能以相同方式定义的任何其他函数。 获得更大的程序,更多的回报。
Note that even though add_num
is written in a functional style, it isn’t purely functional. It has one side effect, numbers
, which makes it an impure function. But that’s okay: you don’t need to be a slave to one programming paradigm; instead, you can get the best of each to maximize your productivity.
请注意,即使add_num
是按功能样式编写的,也不是纯粹功能。 它具有numbers
副作用,这使其功能不纯净。 但这没关系:您不必成为一个编程范例的奴隶。 取而代之的是,您可以充分利用每种技术的优势,以最大限度地提高生产力。
装饰工 (Decorators)
Of course you can combine the two approaches from above and write a function that not only takes a function as an argument, but returns a function as well. Consider this code, where we’re expanding on the add_num
function from above:
当然,您可以从上面结合两种方法并编写一个函数,该函数不仅将函数作为参数,而且还返回一个函数。 考虑下面的代码,我们从上面扩展add_num
函数:
def add_num(message):
def add_inc(increment, numbers):
message()
incremented_nums = []
for n in numbers:
incremented_nums.append(n + increment)
return incremented_nums
return add_incdef message1():
print("Doing something...")message1 = add_num(message1)print(message1(5, [28,93]))# Doing something...
# [33, 98]
One difference to the example above is that you can customize the message that is output on the screen. In a bigger program, you could expand that to account for different error messages, for example.
与上面的示例的不同之处在于,您可以自定义屏幕上输出的消息。 例如,在更大的程序中,您可以扩展它以解决不同的错误消息。
The line message1 = add_num(message1)
is where the magic happens: the name message1
now points to the inner layer of add_num
, i.e., to add_inc
. This is called a decoration.
行message1 = add_num(message1)
是其中魔法发生:名称message1
现在指向的内层add_num
,即, add_inc
。 这称为装饰。
The other difference is that the argument increment
has been pushed downwards; this just makes it easier to handle the next step.
另一个区别是自变量increment
已被向下推; 这只会使下一步操作变得更容易。
We can make the decoration even nicer with the @
syntax (the def add_num
part will stay the same):
我们可以使用@
语法使修饰更好( def add_num
部分将保持不变):
@add_numdef message1():
print("Doing something...")print(message1(5, [28,93]))
In effect, this is just an even neater way of writing a decoration. Note that using decorators doesn’t imply that your code is functional. Rather, decorators are inspired by functional programming, just like nested functions are. The above example isn’t purely functional since it contains two side-effects, but it’s inspired by functional programming nevertheless.
实际上,这只是编写装饰的一种更整洁的方式。 请注意,使用装饰器并不意味着您的代码可以正常工作。 相反,装饰器是受函数编程启发的,就像嵌套函数一样。 上面的示例并不是纯函数式的,因为它包含两个副作用,但是它仍然受到函数式编程的启发。
生成器表达式和列表推导 (Generator expressions and list comprehensions)
List comprehensions and generator expressions are concepts that Python copied from Haskell, a purely functional programming language. Consider the following example, where we’re trying to calculate a few square numbers:
列表推导和生成器表达式是Python从Haskell(一种纯函数式编程语言)复制的概念。 考虑下面的示例,我们试图计算一些平方数:
numbers = [0, 1, 2, 3, 4]
square_numbers = []for x in range(5):
square_numbers.append(x**2)square_numbers # [0, 1, 4, 9, 16]
That’s quite clunky since we need to define two arrays and write a for
loop. A much more concise and elegant way is to do this with a list comprehension:
这很笨拙,因为我们需要定义两个数组并编写一个for
循环。 一种更简洁,更优雅的方法是通过列表理解来做到这一点:
square_numbers = [x**2 for x in range(5)]
square_numbers # [0, 1, 4, 9, 16]
You can select only particular elements by adding an if
condition. For example, let’s say we only want those square numbers that are even:
您可以通过添加if
条件来仅选择特定元素。 例如,假设我们只想要偶数个平方数:
even_square_numbers = [x**2 for x in range(5)
if x%2 == 0]
even_square_numbers # [0, 4, 16]
List comprehensions store all values of a list in the memory. That’s wonderful for small objects, but they would make your program rather sluggish if you’re dealing with large lists. That’s where generator expressions come into play:
列表推导将列表的所有值存储在内存中。 对于小型对象而言,这很棒,但是如果您要处理大型列表,它们会使您的程序变慢。 这就是生成器表达式的作用:
lots_of_square_numbers = (x**2 for x in range(10000))
lots_of_square_numbers # <generator object <genexpr> at 0x1027c5c50>
Generator expressions don’t evaluate the objects immediately. That’s why you just see a funny expression if you try to call them (the exact form of the output depends on your OS). However, they make them accessible for later. You can call an element of a generator expression like this:
生成器表达式不会立即评估对象。 这就是为什么您尝试调用它们时会看到一个有趣的表达式的原因(输出的确切形式取决于您的操作系统)。 但是,它们使它们可供以后使用。 您可以像这样调用生成器表达式的元素:
next(lots_of_square_numbers) # 0
next(lots_of_square_numbers) # 1
next(lots_of_square_numbers) # 4
...
Or you could create a list of the first few elements in the generator expression like so:
或者,您可以像这样在生成器表达式中创建前几个元素的列表:
[next(lots_of_square_numbers) for x in range(10)]# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
As with the other tricks, this doesn’t automatically make your code purely functional. It’s just a concept borrowed from functional programming that can be useful in many situations.
与其他技巧一样,这不会自动使您的代码纯粹起作用。 这只是从函数式编程中借用的一个概念,在许多情况下都可能有用。
小函数和lambda表达式 (Small functions and the lambda expression)
If you want to write a small function, there is nothing wrong in writing it like this:
如果要编写一个小函数 ,则编写这样的东西没有错:
def add_ab(a, b):
return a + b
However, you could also use a lambda
-expression:
但是,您也可以使用lambda
-expression:
add_ab2 = lambda a, b: a + b
It’s practically the same length, and it’s pretty readable once you’ve got used to the syntax. Whether you use it or not really depends on your personal taste. But as we’ll see below, it can be quite handy in certain situations.
它的长度几乎相同,一旦您习惯了语法,就很容易阅读。 是否使用它实际上取决于您的个人品味。 但是,正如我们将在下面看到的,在某些情况下它可能非常方便。
Like with the above, using lambda
-expressions won’t necessarily make your code functional, even though they are a key idea of functional programming.
与上述类似,即使使用lambda
-expressions也不一定会使您的代码正常工作,即使它们是函数式编程的关键思想。
内置Python函数 (Built-in Python functions)
地图() (map())
The function map()
basically returns generator expressions. This is a simple example:
map()
函数基本上返回生成器表达式。 这是一个简单的示例:
numbers = [0, 1, 2, 3, 4]
squared_numbers_map = list(map(lambda x: x**2, numbers))print(squared_numbers_map)# [0, 1, 4, 9, 16]
As you saw earlier, you can do the same thing with a list comprehension. Sometimes your code can be a bit more readable, however, when you use the map()
function.
如前所述,您可以通过列表理解来做同样的事情。 有时候,当您使用map()
函数时,您的代码可能更具可读性。
过滤() (filter())
This is analogous to a list comprehension with an if
clause, for example like so:
这类似于带有if
子句的列表理解,例如:
squared_numbers_map_even = list(filter(lambda x: x%2 == 0, squared_numbers_map))print(squared_numbers_map_even)# [0, 4, 16]
You can also nest map()
and filter()
like so:
您还可以像这样嵌套map()
和filter()
:
squared_numbers_map_even_new = list(filter(lambda x: x%2 == 0, list(map(lambda x: x**2, numbers))))print(squared_numbers_map_even_new)# [0, 4, 16]
枚举() (enumerate())
If you’re looping through a list and you need to keep track of the indexes, enumerate()
is a good option:
如果要遍历列表,并且需要跟踪索引,那么enumerate()
是一个不错的选择:
for num in enumerate(squared_numbers_map_even):
print(num)# (0, 0)
# (1, 4)
# (2, 16)
压缩() (zip())
If you need to create tuples from two lists, you can use zip()
:
如果需要从两个列表创建元组,则可以使用zip()
:
list(zip(['a', 'b', 'c'], (1, 2, 3)))# [('a', 1), ('b', 2), ('c', 3)]
The list()
is wrapped around this expression because zip()
only returns iterables like generator expressions do.
list()
围绕此表达式包装,因为zip()
仅返回生成器表达式一样的可迭代对象。
functools模块 (The functools module)
Sometimes you’ll have a function that takes a few arguments, but you need to fix a few. Consider this simple example:
有时,您会有一个带有一些参数的函数,但是您需要修复一些参数。 考虑以下简单示例:
import functoolsdef add_lots_of_numbers(a, b, c, d):
return a + b + c + dadd_a_and_b_27 = functools.partial(add_lots_of_numbers, c=18, d=9)
add_a_and_b_27(1,2) # 30
There are a few more functions apart from functools.partial()
in this module, but this is by far the most important one. As before, partial()
doesn’t always lead to functional code, but it’s a neat concept that’s borrowed from functional programming.
除了functools.partial()
,此模块中还有其他一些功能,但这是迄今为止最重要的功能。 和以前一样, partial()
并不总是会生成功能代码,但这是从功能编程中借来的一个简洁的概念。
一些简单的技巧可以帮助您 (A few simple tricks can go a long way)
When you started out coding, you probably heard a lot about object-oriented programming, and not very much about functional programming. That does make sense, since object-oriented programming is extremely useful.
在开始编码时,您可能会听到很多有关面向对象的编程的知识,而关于功能编程的知识则不是很多。 这确实是有道理的,因为面向对象的编程非常有用。
But in recent years, we’ve been encountering more and more problems that are easier to crack when you have some skills in functional programming.
但是近年来,我们遇到了越来越多的问题,当您具备一些函数式编程技能时,这些问题就更容易破解。
You don’t need to learn a functional programming language like Elm or Haskell right away. Instead, you can take the most useful aspects of them and use them directly in your Python code.
您不需要立即学习像Elm或Haskell这样的函数式编程语言。 相反,您可以利用它们中最有用的方面,并直接在Python代码中使用它们。
Once you know the tricks, you’ll see opportunities to use them all over the place. Happy coding!
一旦知道了窍门,您将发现在各地使用它们的机会。 编码愉快!
翻译自: https://towardsdatascience.com/how-to-make-your-python-code-more-functional-b82dad274707
功能测试代码python
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388267.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!