功能测试代码python_如何使您的Python代码更具功能性
功能測試代碼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 = 3def 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 functionsnumbers_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 functionsnumbers_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添加一行,而不是上面示例中的三行。
One key concept of functional programming is nesting functions into one another. Photo by ThisisEngineering RAEng on Unsplash函數式編程的一個關鍵概念是函數之間的嵌套。 ThisisEngineering RAEng在Unsplash上拍攝的照片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 can make your code more elegant. Photo by Fernando Hernandez on Unsplash裝飾器可以使您的代碼更加優雅。 費爾南多·埃爾南德斯在Unsplash上的照片裝飾工 (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) # 0next(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 expressions can be a neat alternative to regular function definitions. Photo by NESA by Makers on UnsplashLambda表達式可以替代常規函數定義。 由NESS 由Makers在Unsplash上拍攝小函數和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 + bIt’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
總結
以上是生活随笔為你收集整理的功能测试代码python_如何使您的Python代码更具功能性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spotify歌曲下载_使用Spotif
- 下一篇: 十几年了还是梦到初恋女友