python 33個高級用法技巧
- 使用裝飾器計時函數
裝飾器是一種允許在一個函數或方法調用前后運行額外代碼的結構。
import timedef timer(func):"""裝飾器函數,用于計算函數執行時間并打印。參數:func (function): 被裝飾的函數返回:function: 包裝后的函數"""def wrapper(*args, **kwargs):"""包裝函數,計算并打印函數執行時間。參數:*args: 原函數的非關鍵字參數**kwargs: 原函數的關鍵字參數返回:原函數的返回值"""start_time = time.time() # 記錄開始時間result = func(*args, **kwargs) # 執行原函數end_time = time.time() # 記錄結束時間print(f"Function {func.__name__} took {end_time - start_time} seconds")return result # 返回原函數的結果return wrapper@timer
def example_function(x):"""示例函數,等待 x 秒后返回 x。參數:x (int/float): 等待的秒數返回:int/float: 輸入的 x 值"""time.sleep(x) # 模擬耗時操作return x# 調用被裝飾的函數
result = example_function(2)
print(result)
Function example_function took 2.0022225379943848 seconds
2
-
裝飾器功能:
- 裝飾器
timer
計算并打印被裝飾函數的執行時間。 - 通過
wrapper
函數實現這一功能,使得可以在不修改原函數代碼的情況下,添加額外的行為。
- 裝飾器
-
裝飾器語法糖:
@timer
是裝飾器的簡潔語法,用于將裝飾器應用于函數。- 相當于手動將函數傳遞給裝飾器并將返回值賦給原函數名。
-
包裝函數:
wrapper
函數接受任意數量的參數和關鍵字參數,確保可以包裝任何函數。- 在
wrapper
中,可以在調用原函數之前或之后添加任何額外的代碼,這里是計算并打印執行時間。
- 使用生成器
生成器是一種特殊的迭代器,通過yield
關鍵字逐個生成值。
通過使用裝飾器,可以在不修改原函數代碼的情況下,添加額外的行為。裝飾器函數接受一個函數作為參數,返回一個新的包裝函數,在調用原函數之前或之后執行額外的代碼。裝飾器提供了一種簡潔而強大的方式來擴展函數的功能,使代碼更加模塊化和可重用。
def countdown(n):"""生成從 n 到 1 的倒計時序列。參數:n (int): 倒計時的起始值生成:int: 當前倒計時的值"""while n > 0:yield nn -= 1# 使用生成器函數 countdown 進行倒計時
for i in countdown(5):print(i)
5
4
3
2
1
-
生成器函數:
- 生成器函數使用
yield
關鍵字逐個生成值,與常規的返回值函數不同,它在每次生成值后暫停執行,并保留其狀態以便繼續生成下一個值。 - 在生成器函數
countdown
中,while
循環每次生成當前的n
值,然后將n
減少 1。
- 生成器函數使用
-
迭代生成器:
- 使用
for
循環迭代生成器時,循環會自動處理生成器的狀態,并在每次迭代時調用生成器函數的__next__()
方法,獲取下一個值。 - 當生成器函數不再生成新值時,迭代結束。
- 使用
通過使用生成器函數,可以逐個生成序列中的值,而不需要一次性創建整個序列。這種方法在處理大數據集或需要逐步生成數據時非常有用,能夠節省內存并提高效率。生成器函數使用 yield
關鍵字生成值,并在每次生成后暫停執行,保留其狀態以便后續繼續生成。
- 使用命名元組
命名元組是一種特殊的元組,允許通過名稱訪問元素。
from collections import namedtuplePoint = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y)
10 20
- 使用全局變量
全局變量可以在多個函數之間共享數據。
global_var = 0def increment():global global_varglobal_var += 1increment()
print(global_var)
1
- 使用局部變量
局部變量在函數內部定義,只在函數內部有效。
def example():local_var = 'Hello'print(local_var)example()
# print(local_var) # 這行會報錯,因為local_var是局部變量
Hello
- 使用類方法
類方法是一種綁定到類而不是實例的方法。
class MyClass:class_var = 0@classmethoddef increment_class_var(cls):cls.class_var += 1MyClass.increment_class_var()
print(MyClass.class_var)
1
- 使用靜態方法
靜態方法是一種不依賴于類或實例的獨立方法。
class MyClass:@staticmethoddef static_method():print('This is a static method.')MyClass.static_method()
This is a static method.
- 使用實例方法
實例方法是綁定到實例的方法,可以訪問實例的屬性和方法。
class MyClass:def __init__(self, value):self.value = valuedef display_value(self):print(self.value)obj = MyClass(10)
obj.display_value()
10
- 使用裝飾器添加功能
裝飾器可以在不修改原函數代碼的情況下添加功能。
def decorator(func):"""裝飾器函數,用于在調用被裝飾函數之前打印一條消息。參數:func (function): 被裝飾的函數返回:function: 包裝后的函數"""def wrapper(*args, **kwargs):"""包裝函數,打印一條消息然后調用原函數。參數:*args: 原函數的非關鍵字參數**kwargs: 原函數的關鍵字參數返回:原函數的返回值"""print('Function is called')return func(*args, **kwargs)return wrapper@decorator
def say_hello():"""打印 'Hello' 的函數"""print('Hello')# 調用被裝飾的函數
say_hello()
Function is called
Hello
- 使用鏈式函數調用
鏈式函數調用允許連續調用多個方法。
class MyClass:def __init__(self, value):self.value = valuedef increment(self):self.value += 1return selfdef display_value(self):print(self.value)obj = MyClass(10)
obj.increment().increment().display_value()
12
- 使用自定義迭代器
自定義迭代器可以實現自己的迭代邏輯。
class MyIterator:def __init__(self, data):"""初始化 MyIterator 對象,并設置初始數據和索引。參數:data (list): 要迭代的數據列表"""self.data = dataself.index = 0def __iter__(self):"""返回迭代器對象本身。返回:MyIterator: 迭代器對象"""return selfdef __next__(self):"""返回下一個數據元素。返回:int/float: 當前索引的數據元素拋出:StopIteration: 當沒有更多元素時停止迭代"""if self.index < len(self.data):result = self.data[self.index]self.index += 1return resultelse:raise StopIteration# 創建 MyIterator 對象并迭代打印每個元素
my_iter = MyIterator([1, 2, 3])
for value in my_iter:print(value)
1
2
3
__iter__
方法返回迭代器對象本身。__next__
方法在每次迭代中被調用,返回當前索引位置的元素,并將索引加1。
當索引超出數據列表的長度時,拋出 StopIteration 異常,迭代結束。
- 使用類方法
類方法可以在不實例化類的情況下調用。
class MyClass:class_var = 0@classmethoddef increment_class_var(cls):cls.class_var += 1MyClass.increment_class_var()
print(MyClass.class_var)
1
- 使用屬性裝飾器
屬性裝飾器用于控制屬性的訪問和修改。
class MyClass:def __init__(self, value):"""初始化 MyClass 對象并設置初始值。參數:value (int/float): 初始值"""self._value = value@property # 屬性方法def value(self):"""獲取 _value 的值。返回:int/float: 當前 _value 的值"""return self._value@value.setter # 屬性的設置方法def value(self, new_value):"""設置 _value 的新值。參數:new_value (int/float): 新值"""self._value = new_value# 創建一個 MyClass 對象,初始值為 10
obj = MyClass(10)# 獲取并打印 _value 的值
print(obj.value) # 輸出: 10# 設置 _value 的新值為 20
obj.value = 20# 獲取并打印新的 _value 的值
print(obj.value) # 輸出: 20
10
20
-
@property
裝飾器:- 將方法轉換為屬性,使得可以通過
obj.value
訪問,而不需要調用方法。 - 這種方法使得屬性訪問看起來更自然,與直接訪問實例變量類似。
- 將方法轉換為屬性,使得可以通過
-
@value.setter
裝飾器:- 將方法轉換為屬性的設置方法,使得可以通過
obj.value = new_value
來設置屬性的值。 - 這種方法提供了一種控制屬性值設置的機制,可以在設置值之前進行驗證或其他處理。
- 將方法轉換為屬性的設置方法,使得可以通過
- 使用字典合并
合并兩個字典。
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}# 使用字典的update方法合并
dict1.update(dict2)
print(dict1)
print({**dict1, **dict2})
{'a': 1, 'b': 3, 'c': 4}
{'a': 1, 'b': 3, 'c': 4}
- 使用
Counter
計數
Counter
類用于計數可哈希對象。
from collections import Counterdata = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(data)
print(counter)
Counter({'apple': 3, 'banana': 2, 'orange': 1})
- 使用
deque
進行雙端隊列操作
deque
是一種雙端隊列,可以在兩端高效地添加和刪除元素。
from collections import dequed = deque([1, 2, 3])
d.appendleft(0)
d.append(4)
print(d)
deque([0, 1, 2, 3, 4])
- 使用
defaultdict
defaultdict
是一個帶有默認值的字典。
from collections import defaultdictdd = defaultdict(int)
dd['a'] += 1
print(dd)
defaultdict(<class 'int'>, {'a': 1})
- 使用堆排序
使用heapq
模塊進行堆排序。
import heapq# 初始化一個無序列表
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]# 使用 heapq.heapify 將列表轉換為堆
heapq.heapify(data)# 使用 heapq.heappop 逐個彈出最小元素,實現排序
sorted_data = [heapq.heappop(data) for _ in range(len(data))]# 打印排序后的列表
print(sorted_data)
[1, 1, 2, 3, 4, 5, 5, 6, 9]
-
堆排序過程:
heapq.heapify(data)
將列表data
轉換為最小堆,最小元素在堆的根節點。heapq.heappop(data)
逐個彈出最小元素,重新調整堆結構,使得次小元素成為新的根節點。- 通過列表推導式,所有元素依次彈出并存入新的列表
sorted_data
,實現排序。
-
堆的性質:
- 最小堆是一種完全二叉樹結構,滿足父節點小于或等于子節點的性質。
- 這種結構使得獲取最小元素的時間復雜度為
O(1)
,插入和刪除元素的時間復雜度為O(log n)
。
import heapq# 初始化一個無序列表
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]# 將所有元素取反,構建最大堆
max_heap = [-x for x in data]# 使用 heapq.heapify 將列表轉換為堆
heapq.heapify(max_heap)# 使用 heapq.heappop 逐個彈出最大元素(原值)
sorted_data = [-heapq.heappop(max_heap) for _ in range(len(max_heap))]# 打印排序后的列表
print(sorted_data)
[9, 6, 5, 5, 4, 3, 2, 1, 1]
通過使用 heapq
模塊,可以間接實現大頂堆和堆排序。雖然 heapq
主要支持最小堆,但通過取反數的方法,可以高效地實現最大堆排序。
- 使用
bisect
進行二分查找
使用bisect
模塊進行二分查找。
import bisect# 初始化一個有序列表
data = [1, 2, 4, 4, 5]# 使用 bisect.insort 在合適的位置插入元素 3
bisect.insort(data, 3)# 打印插入后的列表
print(data)
[1, 2, 3, 4, 4, 5]
-
二分查找:
bisect
模塊使用二分查找算法在有序列表中找到元素應該插入的位置。- 二分查找的時間復雜度為
O(log n)
,比線性查找O(n)
更高效。
-
插入元素:
insort
函數不僅找到插入位置,還會將元素插入到該位置。
- 使用
itertools
生成排列組合
import itertools# 生成 'ABC' 字符串的長度為2的排列
permutations = list(itertools.permutations('ABC', 2))
# 生成 'ABC' 字符串的長度為2的組合
combinations = list(itertools.combinations('ABC', 2))print('Permutations:', permutations)
print('Combinations:', combinations)
Permutations: [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
Combinations: [('A', 'B'), ('A', 'C'), ('B', 'C')]
- 使用
itertools
生成無限序列
import itertoolscounter = itertools.count(start=1, step=2)
print(next(counter))
print(next(counter))
print(next(counter))
1
3
5
- 使用
functools.partial
使用partial
函數創建部分參數的函數。
from functools import partialdef power(base, exponent):"""計算 base 的 exponent 次方。參數:base (int/float): 底數exponent (int/float): 指數返回:int/float: base 的 exponent 次方"""return base ** exponent# 使用 partial 函數創建一個新的函數 square,固定 exponent 參數為 2
square = partial(power, exponent=2)# 計算 3 的平方
print(square(3)) # 輸出: 9
9
partial
函數:
partial
函數用于固定一個函數的部分參數,從而創建一個新的函數。
在這個例子中,partial
被用來固定power
函數的exponent
參數為 2,從而創建一個新的函數square
。
- 使用
functools.lru_cache
使用lru_cache
緩存函數結果,提高性能。
from functools import lru_cache@lru_cache(maxsize=None)
def fibonacci(n):"""計算第n個斐波那契數,使用lru_cache進行緩存以提高性能。參數:n (int): 需要計算的斐波那契數的索引返回:int: 第n個斐波那契數"""if n < 2:return nreturn fibonacci(n-1) + fibonacci(n-2)# 打印第10個斐波那契數
print(fibonacci(10))
55
-
緩存的優勢:
- 在沒有緩存的情況下,遞歸計算斐波那契數會有大量的重復計算。例如,計算
F(10)
會多次計算F(9)、F(8)
等。 - 使用
lru_cache
后,每個值只計算一次,然后存儲在緩存中,以后再需要相同值時直接從緩存中讀取,避免重復計算,提高了性能。
- 在沒有緩存的情況下,遞歸計算斐波那契數會有大量的重復計算。例如,計算
-
遞歸過程:
- 當計算
fibonacci(10)
時,函數會遞歸調用fibonacci(9)
和fibonacci(8)
,依次類推,直到調用fibonacci(0)
和fibonacci(1)
。 - 由于
lru_cache
的存在,計算fibonacci(10)
的整個過程中,每個值只會計算一次,并存儲在緩存中。
- 當計算
- 使用
subprocess
運行外部命令
import subprocessresult = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)
print(result.stdout)
Hello, World!
- 使用
shutil
進行文件操作
import shutil
# 創建一個文件 example.txt 并寫入內容
with open('example.txt', 'w') as file:file.write('Hello, world!')
shutil.copy('example.txt', 'example_copy.txt')
print('File copied.')
File copied.
- 使用
pathlib
處理文件路徑
from pathlib import Path# 創建一個文件 example.txt 并寫入內容
with open('example.txt', 'w') as file:file.write('Hello, world!')# 使用 pathlib 創建一個 Path 對象
p = Path('example.txt')# 打印文件名
print(p.name) # 輸出:example.txt# 打印文件名(不包括后綴)
print(p.stem) # 輸出:example# 打印文件后綴
print(p.suffix) # 輸出:.txt# 打印文件的父目錄
print(p.parent) # 輸出:.
example.txt
example
.txt
.
- 使用正則表達式匹配字符串
使用re
模塊進行正則表達式匹配。
import repattern = re.compile(r'\d+')
match = pattern.search('The answer is 42')
print(match.group())
42
- 使用內存映射文件
使用mmap
模塊進行內存映射文件操作。
import mmap
# 創建一個文件 example.txt 并寫入內容
with open('example.txt', 'w') as file:file.write('Hello, world!')
# 打開文件 example.txt 進行讀寫操作 ('r+b' 表示讀寫二進制模式)
with open('example.txt', 'r+b') as f:# 使用 mmap 模塊創建內存映射對象with mmap.mmap(f.fileno(), 0) as mm:# 從內存映射對象中讀取一行,并將其解碼為 UTF-8 字符串print(mm.readline().decode('utf-8'))
Hello, world!
with mmap.mmap(f.fileno(), 0) as mm:
使用 mmap
模塊創建一個內存映射對象。f.fileno()
返回文件的文件描述符,0
表示將整個文件映射到內存中。with
語句確保內存映射對象在塊結束時會自動關閉。
- 使用logging記錄日志
import logginglogging.basicConfig(level=logging.INFO)
logging.info('This is an info message')
INFO:root:This is an info message
- 使用
argparse
解析命令行參數
import argparsedef main(name="Default Name"):print(f"Hello, {name}!")if __name__ == "__main__":parser = argparse.ArgumentParser(description='Example script')parser.add_argument('name', type=str, nargs='?', default="Default Name", help='Your name')args = parser.parse_args()main(args.name)
Hello, Alice!
- 使用
unittest
進行單元測試
import unittestdef add(x, y):return x + yclass TestAdd(unittest.TestCase):def test_add(self):self.assertEqual(add(2, 3), 5)if __name__ == '__main__':unittest.main(argv=[''], verbosity=2, exit=False)
test_add (__main__.TestAdd) ... ok----------------------------------------------------------------------
Ran 1 test in 0.002sOK
- 使用
tqdm
顯示進度條
from tqdm import tqdm
import timefor i in tqdm(range(100)):time.sleep(0.01)
100%|██████████| 100/100 [00:01<00:00, 97.88it/s]
- 使用
pandas
進行數據分析
import pandas as pddata = {'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35]}
df = pd.DataFrame(data)
print(df)
Name Age
0 Alice 25
1 Bob 30
2 Charlie 35