首頁  >  文章  >  後端開發  >  Python列表解析式到底該怎麼用​​?

Python列表解析式到底該怎麼用​​?

王林
王林轉載
2023-04-16 21:10:06821瀏覽

Python 是一種極其多樣化和強大的程式語言!當需要解決一個問題時,它有不同的方法。在本文中,將會展示列表解析式(List Comprehension)。我們將討論如何使用它?什麼時候該或不該使用它?

Python列表解析式到底該怎麼用​​?

列表解析式的優點

  • 比迴圈更節省時間和空間。
  • 需要更少的程式碼行。
  • 可將迭代語句轉換為公式。

如何在Python中建立清單

清單解析式是一種基於現有清單建立清單的語法結構。讓我們來看看創建列表的不同實作

循環

循環是創建列表的傳統方式。不管你使用什麼樣的循環。要以這種方式建立列表,您應該:

  1. 實例化一個空列表。
  2. 循環遍歷一個可迭代的(如range)的元素。
  3. 將每個元素附加到清單的末端。
numbers = []
for number in range(10):
numbers.append(number)

print(numbers)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

在此範例中,您實例化了一個空列表 numbers。然後使用 for 循環迭代 range(10) 並使用 append() 方法將每個數字附加到清單的末端。

map() 物件

map() 是建立清單的另一種方法。您需要向 map() 傳遞一個函數和一個可迭代對象,之後它會建立一個物件。此物件包含使用指定函數執行每個迭代元素所獲得的輸出。

例如,我們將呈現在某些產品的價格中增加增值稅的任務。

VAT_PERCENT = 0.1# 10%


def add_vat(price):
return price + (price * VAT_PERCENT)


prices = [10.03, 8.6, 32.85, 41.5, 22.64]
grand_prices = map(add_vat, prices)
print(grand_prices)
grand_prices = list(grand_prices)
print(grand_prices)

您已經建立了 add_vat() 函數並建立了 prices 可迭代物件。您將這兩個參數都傳遞給 map() 並收集產生的 map 物件 grand_prices,或者您可以使用 list() 輕鬆地將其轉換為清單。

<map object at 0x7f18721e7400># map(add_vat, prices)
[11.03, 9.46, 36.14, 45.65, 24.9]# list(grand_prices)

列表解析式

現在,讓我們來看看列表解析式方法!這確實是 Python 風格,並且是建立清單的更好方法。為了弄清楚這種方法有多強大,我們用一個單行程式碼來重寫那個循環範例。

numbers = [number for number in range(10)]
print(numbers)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

如您所見,這是一種不可思議的方法!清單解析式看起來足夠可讀,您不需要編寫更多程式碼,而只需一行。

為了更好地理解列表,請查看以下語法格式:

new_list = [expression for member in iterable]

哪種方法更有效

好的,我們已經學習瞭如何使用循環、map( ) 和列表解析式來建立列表,在您的腦海中可能會提出「哪種方法更有效」的問題。我們來分析一下吧!

import random
import timeit


VAT_PERCENT = 0.1
PRICES = [random.randrange(100) for x in range(100000)]


def add_vat(price):
return price + (price * VAT_PERCENT)


def get_grand_prices_with_map():
return list(map(add_vat, PRICES))


def get_grand_prices_with_comprehension():
return [add_vat(price) for price in PRICES]


def get_grand_prices_with_loop():
grand_prices = []
for price in PRICES:
grand_prices.append(add_vat(price))
return grand_prices


print(timeit.timeit(get_grand_prices_with_map, number=100))
print(timeit.timeit(add_grand_prices_with_comprehension, number=100))
print(timeit.timeit(get_grand_prices_with_loop, number=100))
0.9833468980004909# with_map
1.197223742999995 # with_comprehension
1.3564663889992516# with_loop

正如我們現在所看到的,創建列表的最優的方法是 map(),排在第二位的是列表解析式,最後是循環。

但是,方法的選擇應取決於您想要實現的目標。

  • 使用map() 可以讓你的程式碼更有效率。
  • 使用循環可以讓程式碼的思路展現更加清晰。
  • 使用清單解析式可以您讓程式碼更加緊湊,且更有效率。這是建立清單的最佳方式,因為這種方式可讀性最強。

高級解析式

條件邏輯

早些時候,我向您展示了這個公式:

new_list = [expression for member in iterable]

公式可能有些不完整。對解析式的更完整描述增加了對可選條件的支援。將條件邏輯新增至清單解析式最常見的方法是在表達式的末端新增條件:

new_list = [expression for member in iterable (if conditional)]

在這裡,您的條件語句正好位於右邊的括號中。

條件很重要,因為它們允許列表解析式過濾掉不需要的值,這在一般情況下也可以呼叫 filter():

numbers = [number for number in range(20) if number % 2 == 0]
print(numbers)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

正如您所看到的那樣,這個解析式收集了可被2 整除且沒有餘數的數字。

如果您需要更複雜的篩選器,那麼您甚至可以將條件邏輯移到單獨的函數中。

def is_prime(number):
if number > 1:
for el in range(2, int(number/2)+1):
if (number % el) == 0:
return False 
else:
return True


numbers = [number for number in range(20) if is_prime(number)]
print(numbers)
[2, 3, 5, 7, 11, 13, 17, 19]

您建立 is_prime(number) 以確定是否是素數並傳回布林值。接下來,您應該將函數新增到解析式的條件中。

該公式可讓您使用條件邏輯從幾個可能的輸出選項中進行選擇。例如,您有一個產品價格表,若有負數,您應將其轉換為正數:

price_list = [1.34, 19.01, -4.2, 6, 8.78, -1,1]
normalized_price_list = [price if price > 0 else price*-1 for price in price_list]
print(normalized_price_list)
[1.34, 19.01, 4.2, 6, 8.78, 1,1]

在這裡,您的表達式 price 有一個條件語句,如果 price > 0 else price* -1。這會告訴 Python,如果價格為正,則輸出價格值;但如果價格為負,則將價格轉換為正值。該功能很強大,考慮將條件邏輯視為其自身的函數的確是很有用的:

def normalize_price(price):
return price if price > 0 else price*-1


price_list = [1.34, 19.01, -4.2, 6, 8.78, -1,1]
normalized_price_list = [normalize_price(price) for price in price_list]
print(normalized_price_list)
[1.34, 19.01, 4.2, 6, 8.78, 1,1]

集合解析式

您还可以创建一个集合解析式!它基本与列表解析式相同。不同之处在于集合解析式不包含重复项。您可以通过使用花括号取代方括号来创建集合解析式:

string = "Excellent"
unique_string = {letter for letter in string}
print(unique_string)
{"E", "e", "n", "t", "x", "c", "l"}

你的集合解析式只包含唯一的字母。这与列表不同,集合不保证项目将以特定顺序存储数据。这就是为什么集合输出的第二个字母是 e​,即使字符串中的第二个字母是 x。

字典解析式

字典解析式也是是类似的,但需要定义一个键:

string = "Words are but wind"
word_order = {el: ind+1 for ind, el in enumerate(string.split())}
print(word_order)
{"Words": 1, "are": 2, "but": 3, "wind": 4}

要创建 word_order​ 字典,请在表达式中使用花括号 ({}​) 以及键值对 (el: ind+1)。

海象运算符

Python 3.8 中引入的海象运算符允许您一次解决两个问题:为变量赋值,返回该值。

假设您需要对将返回温度数据的 API 应用十次。您想要的只是 100 华氏度以上的结果。而每个请求可能都会返回不同的数据。在这种情况下,没有办法在 Python 中使用列表解析式来解决问题。可迭代成员(如果有条件)的公式表达式无法让条件将数据分配给表达式可以访问的变量。

海象运算符解决了这个问题。它允许您在执行表达式的同时将输出值分配给变量。以下示例显示了这是如何实现的,使用 get_weather_data() 生成伪天气数据:

import random


def get_weather_data():
return random.randrange(90, 110)


hot_temps = [temp for item in range(20) if (temp := get_weather_data()) >= 100]
print(hot_temps)
[108, 100, 106, 103, 108, 106, 103, 104, 109, 106]

什么时候不要使用解析式

列表解析式非常有用,它可以帮助您编写清晰且易于阅读和调试的代码。但在某些情况下,它们可能会使您的代码运行速度变慢或使用更多内存。如果它让您的代码效率更低或更难理解,那么可以考虑选择另一种方式。

注意嵌套的解析式

可以通过嵌套解析式以创建列表、字典和集合的组合集合(译者注:这个集合不是指 set 对象类型,而是 collection,泛指容器)。例如,假设一家公司正在跟踪一年中五个不同城市的收入。存储这些数据的完美数据结构可以是嵌套在字典解析式中的列表解析式。

cities = ['New York', 'Oklahoma', 'Toronto', 'Los Angeles', 'Miami']
budgets = {city: [0 for x in range(12)] for city in cities}
print(budgets)
{
"NewYork": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"Oklahoma": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"Toronto": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"LosAngeles": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"Miami": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}

您使用字典解析式创建了 budgets​ 容器。该表达式是一个键值对,其中包含另一个解析式。此代码将快速生成城市中每个 city 的数据列表。

嵌套列表是创建矩阵的常用方法,通常用于数学目的。查看下面的代码块:

matrix = [[x for x in range(7)] for y in range(6)]
print(matrix)
[
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6]
]

外部列表解析式 [... for y in range(6)]​ 创建了六行,而内部列表解析式 [x for x in range(7)] 将用值填充这些行中的每一行。

到目前为止,每个嵌套解析式的目标都是真正且直观的。但是,还有一些其他情况,例如创建扁平化的嵌套列表,其中的逻辑可以使您的代码非常难以阅读。让我们看下面的例子,使用嵌套列表解析式来展平一个矩阵:

matrix = [
[0, 1, 0],
[1, 0, 1],
[2, 1, 2],
]
flat = [num for row in matrix for num in row]
print(flat)
[0, 1, 0, 1, 0, 1, 2, 1, 2]

扁平化矩阵的代码确实很简洁,但是太难理解了,您应该花点时间弄清楚它是如何工作的。另一方面,如果您使用 for 循环来展平相同的矩阵,那么您的代码将更加简单易读:

matrix = [
[0, 1, 0],
[1, 0, 1],
[2, 1, 2],
]
flat = []
for row in matrix:
for num in row:
flat.append(num)
print(flat)
[0, 1, 0, 1, 0, 1, 2, 1, 2]

现在,您可以看到代码一次遍历矩阵的一行,在移动到下一行之前取出该行中的所有元素。

虽然嵌套列表解析式可能看起来更具有 Python 风格,但对于能够编写出您的团队可以轻松理解和修改的代码来才是更加最重要的。当选择一个方法时,您应该根据解析式是有助于还是有损于可读性来做出相应的判断。

为大型数据集使用生成器

Python 中的列表解析式通过将整个列表存储到内存中来工作。对于小型至中型列表这通常很好。如果您想将前一千个整数相加,那么列表解析式将轻松地解决此任务:

summary = sum([x for x in range(1000)])
print(summary)
499500

但是,如果您需要对十亿个数字求和呢?您可以尝试执行此操作,但您的计算机可能不会有响应。这是可能因为计算机中分配大量内存。也许您是因为计算机没有如此多的内存资源。

例如,你想要一些第一个十亿整数,那么让我们使用生成器!这可能多需要一些时间,但计算机应该可以克服它:

summary = sum((x for x in range(1000000000)))
print(summary)
499999999500000000

让我们来对比一下哪种方法是更优的!

import timeit


def get_sum_with_map():
return sum(map(lambda x: x, range(1000000000)))


def get_sum_with_generator():
return sum((x for x in range(1000000000)))


print(timeit.timeit(get_sum_with_map, number=100))
print(timeit.timeit(get_sum_with_generator, number=100))
4940.844053814# get_sum_with_map
3464.1995523349997# get_sum_with_generator

正如您所见,生成器比 map() 高效得多。

总结

本文向您介绍了列表解析式,以及如何使用它来解决复杂的任务,而不会使您的代码变得过于困难。

現在你:

  • 學習了幾種創建清單的替代方法。
  • 找出每種方法的優點。
  • 可以簡化迴圈和 map() 呼叫列表解析式。
  • 了解一種將條件邏輯加入解析式中的方法。
  • 可以建立集合和字典解析式。
  • 學會了何時不使用解析式。

以上是Python列表解析式到底該怎麼用​​?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除