【編者按】這篇文章主要介紹了一些在 Python 程式設計中可能被忽視的核心功能,包括預設參數、海象運算符、*args 和 **kwargs 的使用、變量交換、str 與 repr 的差別、可疊代對象的擴充解包、多個上下文管理器的使用、Python 調試器、collections.Counter 的使用、itertools 的使用以及下劃線的兩種用法等。
原文連結:https://erikvandeven.medium.com/python-uncovering-the-overlooked-core-functionalities-54590420c225
未經允許,禁止轉載!
作者 | Erik van de Ven 譯者 | 明明如月責編 | 夏萌
出品 | CSDN(ID:CSDNnews)
圖檔由 Stefan Steinbauer 提供,釋出在 Unsplash 上
Kyle Simpson 在他的書中提到的關于 JavaScript 的觀點,Luciano Ramalho 在他的《Fluent Python》一書中對 Python 的描述也有相同的主題。他們基本上是在讨論這兩種語言的同一個問題。用我的話來說:
因為這門語言非常容易學,許多實踐者隻是了解了它的基礎知識,而忽略了深入探讨這門語言更進階和強大的方面,這也是它真正獨特和強大的地方。
是以,讓我們簡要讨論一下所有你可能還沒有聽說過的功能,但如果你想成為一名真正經驗豐富的 Pythonista,你肯定想了解。
參數預設值
需要重點注意的是,Python 的參數在遇到函數定義時就進行評估。這意味着每次調用 fib_memo 函數(下文會提到)而沒有明确提供 memo 參數的值時,它将使用函數定義時建立的相同字典對象。
def fib_memo(n, memo={0:0, 1:1}): """ n 是你想要傳回的序列中的第 n 個數字 """ if not n in memo: memo[n] = fib_memo(n-1) + fib_memo(n-2) return memo[n] # 傳回一個介于0和100之間(包括0和100)的數字。 fib_memo(6) # 應該傳回8
這段代碼在 Python 中可以正常運作。這也意味着你可以在一個腳本中多次執行 fib_memo 函數,比如在一個 for 循環中,每次執行都會增加要計算的 fibonacci 數字,而不會達到“超過最大遞歸深度”的限制,因為 memo 會不斷擴充。關于這方面的更多資訊可以在我的另一篇文章中找到。
海象操作符
海象操作符 (:= ) 是在 Python 3.8 中引入的,它允許你在表達式中為變量指派。這樣,你可以在一個表達式中為變量指派并檢查其值:
import random some_value = random.randint(0,100) # 傳回一個介于0和100之間(包括0和100)的數字。 if((below_ten := some_value) < 10): print(f"{below_ten} 小于 10")
顯然,它也可以友善地指派并檢查傳回的值是否包含真值:
if(result := some_method()): # 如果結果不為假值(Falsy) print(result)
*args 和 **kwargs
通過星号 (* ),你可以在傳遞給函數之前解包參數或關鍵字參數(使用 ** )。
例如,考慮以下代碼:
my_numbers = [1,2] def sum_numbers(first_number, second_number): return first_number + second_number # 這将傳回一個類型錯誤。 # 類型錯誤:sum() 缺少 1 個必需的位置參數:“second_number” sum_numbers(my_numbers) #這将傳回預期的結果,3。 sum_numbers(*my_numbers)
當我們調用 sum_numbers 函數時,如果不解包 my_numbers,它會引發一個 TypeError ,因為函數期望兩個獨立的參數。然而,通過使用星号 (*),我們可以從 my_numbers 中解包值并将它們作為獨立的參數傳遞,進而得到正确的輸出。
這種解包技術不僅适用于元組和清單,還适用于字典(盡管它會将鍵作為參數傳遞)。那麼關鍵字參數呢?對此,我們可以利用雙星号 (**)。以下代碼作為例子:
def greet_person(last_name, first_name): print(f"Hello {first_name} {last_name}") data = {"first_name": "John", "last_name": "Doe"} greet_person(**data)
除了解包一個序列來将它們作為函數的參數,你也可以用它來建立一個新的序列,例如:
numbers = [1, 2, 3, 4, 5] new_list_numbers = [*numbers]
原始的 numbers 清單保持不變,而你有一個 new_list_numbers 變量,它包含了相同清單的副本。然而,對于包含對象的連結要小心:
numbers = [[1, 2], [3, 4], [5, 6]] packed_numbers = [*numbers] numbers[0].append(10) # 修改原始清單中的嵌套清單 print(numbers) # 輸出: [[1, 2, 10], [3, 4], [5, 6]] print(packed_numbers) # 輸出: [[1, 2, 10], [3, 4], [5, 6]]
any 和 all
any 和 all 是兩個内建函數,它們對可疊代對象(如清單、元組或集合)進行操作,并基于可疊代對象中的元素傳回一個布爾值。
例如:
some_booleans = [True, False, False, False] any(some_booleans) # 傳回 True all(some_booleans) # 傳回 False
你可以将 all 和 any 函數與清單推導式結合使用,它傳回一個可疊代的結果并将其作為參數傳遞給 all 函數:
numbers = [5, 10, 3, 8, -2] all_positive = all(num > 0 for num in numbers)
… 或 any 函數:
fruits = ['apple', 'banana', 'cherry', 'durian'] # 檢查是否所有水果都以“a”開頭 result = all(fruit.startswith('a') for fruit in fruits) print(result)
下面的表格顯示了根據可疊代對象中的值傳回的輸出的不同。
變量交換
你可以組合元組打包(在等号 (=) 右邊發生)和解包(在等号 (=) 左邊發生),并利用這個功能進行變量交換:
a = 10 b = 5 # 通過打包和解包交換 b 和 a 的值 a, b = b, a print(a) #5 print(b) #10
str vs repr
我們習慣于使用 str(some_value) 将某個變量或值轉換為字元串,以便于進行調試列印。我想讓你了解 repr(some_value)。主要的差別是 repr 嘗試傳回對象的可列印表示,而 str 隻嘗試傳回一個字元串表示。
下面是一個更好的例子:
import datetime today = datetime.datetime.now() print(str(today))print(str(today)) # 輸出: 2023-07-20 15:30:00.123456 print(repr(today)) # 輸出:datetime.datetime(2023, 7, 20, 15, 30, 0, 123456)
如你所見,str() 簡單地将 datetime 作為一個字元串表示傳回。如果你想确定變量 today 是否包含一個字元串還是一個 datetime 對象,你無法單獨從這個資訊中得到答案。另一方面,repr() 提供了有關變量所持有的實際對象的資訊。在調試過程中,這個資訊會更有價值。
擴充的可疊代對象解包
這個可以簡單了解:如果你想通過一個指令擷取序列的第一個和最後一個值:
first, *middle, last = [1, 2, 3, 4, 5] print(first) # 1 print(middle) # [2, 3, 4] print(last) # 5
但這也是可行的
*the_first_three, second_last, last = [1, 2, 3, 4, 5] print(the_first_three) # [1, 2, 3] print(second_last) # 4 print(last) # 5
或者其他組合。
多個上下文管理器
我們習慣于一次使用一個上下文管理器,比如打開一個檔案:
with open('file.txt', 'r') as file: # 使用該檔案的代碼 # 該檔案将在塊結束時自動關閉 # 即使發生異常 # 示例:從檔案中讀取行 for line in file: print(line.strip()) with open('file_2.txt', 'r') as other_file: # 第二個上下文管理器 for line in other_file: print(line.strip())
但我們可以輕易地在一個語句中打開多個檔案。如果你想将一行寫入另一個檔案,這非常簡便:
with open('file1.txt') as file1, open('file2.txt') as file2: # 同時使用 file1 和 file2 的代碼 # 檔案将在塊結束時自動關閉 # 即使發生異常 # 示例:從檔案 1 讀取行并将其寫入檔案 2 for line in file1: file2.write(line)
Python 調試器
我們可以在我們的檔案中列印大量的變量進行調試,或者我們可以簡單地使用 Python 調試器 (pdb),它幫助我們設定斷點,使得操作更加簡單:
import pdb # 在你的代碼中設定這個斷點 pdb.set_trace()
使這個功能更有價值的是,程式會在你設定的斷點處停止,你可以列印任何變量來檢查其在特定斷點處的值或存在情況。試試看!當程式觸發一個斷點時,你可以使用以下幾個指令:
- n 或 next:執行下一行。
- s 或 step:步入函數調用。
- c 或 continue:繼續執行直到下一個斷點。
- l 或 list:顯示目前的代碼上下文。
- p <expression> 或 pp <expression>:列印表達式的值。
- b <line> 或 break <line>:在指定的行設定一個新的斷點。
- h 或 help:擷取關于 pdb 的使用幫助。
- q 或 quit:退出調試器并終止程式。
collections.Counter
collections 子產品中的 Counter 類提供了一種便捷的方式來計算可疊代對象中的元素個數:
Counter``collections
from collections import Counter my_list = [1, 2, 3, 1, 2, 1, 3, 4, 5] counts = Counter(my_list) print(counts) # 輸出: 計數器({1: 3, 2: 2, 3: 2, 4: 1, 5: 1})
使用 Itertools 實作組合
我們可以組合不同的 for 循環來建立排列、組合或笛卡爾積,或者我們可以簡單地使用内建的 itertools。
Permutations(排列)
import itertools # 生成排列 perms = itertools.permutations([1, 2, 3], 2) print(list(perms)) # 輸出: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
Combinations(組合)
import itertools # 生成組合 combs = itertools.combinations('ABC', 2) print(list(combs)) # 輸出: [('A', 'B'), ('A', 'C'), ('B', 'C')]
Cartesian product(笛卡爾積)
import itertools # 生成笛卡爾積 cartesian = itertools.product('AB', [1, 2]) print(list(cartesian)) # 輸出: [('A', 1), ('A', 2), ('B', 1), ('B', 2)]
下劃線的兩種用法
下面是在 Python 中使用下劃線的兩種方式:作為大數字的分隔符或作為丢棄變量。
丢棄變量
下劃線 _ 可以用作丢棄變量,用來丢棄不想要的值:
# 忽略函數的第一個傳回值 _, result = some_function() # 不使用循環變量進行循環 for _ in range(5): do_something() # 你隻需要第一個和最後一個 first, *_, last = [1, 2, 3, 4, 5]
大數字的分隔符
在處理大數字值時,你可以使用下劃線 (_) 作為視覺分隔符以提高可讀性。這個特性在 Python 3.6 中被引入,被稱為 "下劃線字面量"。
population = 7_900_000_000 revenue = 3_249_576_382.50 print(population) # 輸出: 7900000000 print(revenue) # 輸出: 3249576382.5
你知道 Python 中還有哪些容易被忽略的好用功能或技巧?歡迎在評論區留言分享。
粉絲福利: