Tối ưu hiệu năng Python: Kỹ thuật và Thực tiễn tốt nhất

By JoeVu, at: 15:56 Ngày 28 tháng 5 năm 2023

Thời gian đọc ước tính: __READING_TIME__ phút

Python Performance Optimization: Techniques and Best Practices
Python Performance Optimization: Techniques and Best Practices

Giới thiệu

 

Python được biết đến với sự đơn giản và dễ đọc, nhưng đôi khi nó có thể gặp phải các vấn đề về hiệu năng. Điều này chủ yếu là do sự thiếu kinh nghiệm hoặc hiểu sai về hành vi của Python. Trong bài viết này, chúng ta sẽ khám phá các vấn đề về hiệu năng phổ biến trong Python và thảo luận về các kỹ thuật và các thực tiễn tốt nhất để tối ưu hóa mã Python của bạn. Bằng cách hiểu các vấn đề này và triển khai các giải pháp được đề xuất, bạn có thể cải thiện đáng kể hiệu năng của các ứng dụng Python.

 

Vấn đề: Vòng lặp không hiệu quả

 

Hãy xem xét ví dụ bên dưới

 

numbers = [1, 2, 3, 4, 5]
result = 0
for num in numbers:
    result += num


Giải pháp: Sử dụng List Comprehension hoặc Generator Expressions

 

numbers = [1, 2, 3, 4, 5]
result = sum(numbers)


Ưu điểm

 

  • List comprehension hoặc generator expressions có thể thực hiện các thao tác lặp hiệu quả hơn.
     
  • Chúng loại bỏ nhu cầu tạo các list tạm thời, dẫn đến giảm mức tiêu thụ bộ nhớ.
     
  • Cú pháp ngắn gọn giúp tăng cường khả năng đọc mã.

 

Nhược điểm

 

  • List comprehensions có thể trở nên phức tạp và làm giảm độ rõ ràng của mã nếu được sử dụng quá nhiều.
     
  • Generator expressions có thể không phù hợp nếu thứ tự các phần tử là rất quan trọng.

 

Một vài ví dụ khác được liệt kê bên dưới

 

Ví dụ 2: Tính toán dư thừa

 

numbers = [1, 2, 3, 4, 5]
total = 0
for num in numbers:
   total += num * 2  # Phép nhân dư thừa với 2


Giải pháp: Thực hiện phép tính bên ngoài vòng lặp nếu nó không phụ thuộc vào biến vòng lặp.

 

# Giải pháp 1: Di chuyển phép tính ra ngoài vòng lặp
numbers = [1, 2, 3, 4, 5]
total = sum(numbers) * 2

 

# Giải pháp 2: Sử dụng List Comprehensive
numbers = [1, 2, 3, 4, 5]
total = sum([number * 2 for number in numbers])

 

Vấn đề: Nối chuỗi quá mức

 

Nối chuỗi quá mức đề cập đến việc thực hiện không hiệu quả việc nối chuỗi nhiều lần bằng toán tử + hoặc toán tử +=. Điều này có thể dẫn đến hiệu năng kém và phân bổ bộ nhớ không cần thiết, đặc biệt là khi xử lý các chuỗi lớn hoặc trong vòng lặp. 

 

Vấn đề với việc nối chuỗi quá mức phát sinh bởi vì các chuỗi trong Python là bất biến, nghĩa là chúng không thể được sửa đổi tại chỗ. Khi nối chuỗi bằng toán tử + hoặc toán tử +=, một đối tượng chuỗi mới được tạo ra mỗi lần, dẫn đến phân bổ bộ nhớ và sao chép thêm.


Hãy xem xét ví dụ bên dưới

 

result = ""
for i in range(1000):
    result += str(i)


Giải pháp: Sử dụng Join hoặc Định dạng chuỗi

 

result = ''.join(str(i) for i in range(1000))


Ưu điểm

 

  • Phương thức join hoặc định dạng chuỗi bằng các dấu giữ chỗ (`%s` hoặc `{}`) hiệu quả hơn cho việc nối chuỗi.
  • Chúng làm giảm số lượng bản sao chuỗi, dẫn đến hiệu năng được cải thiện.

Nhược điểm

  • Định dạng chuỗi có thể khó đọc hơn nếu được sử dụng quá nhiều hoặc trong các kịch bản phức tạp.
  • Một vài ví dụ khác được liệt kê bên dưới

Ví dụ 2: Nối chuỗi quá mức trong việc xây dựng URL

base_url = "https://example.com/api/data?"
parameters = {'param1': 'value1', 'param2': 'value2', ...}
url = base_url
for key, value in parameters.items():
    url += key + '=' + value + '&'


Giải pháp 2: Sử dụng urllib.parse.urlencode()

from urllib.parse import urlencode

base_url = "https://example.com/api/data?"
parameters = {'param1': 'value1', 'param2': 'value2', ...}
url = base_url + urlencode(parameters)


Ví dụ 3: Nối chuỗi quá mức trong việc tạo CSV

data = [['Name', 'Age', 'Country'], ['John', '25', 'USA'], ...]
csv_content = ""
for row in data:
    csv_content += ','.join(row) + '\n'


Giải pháp 3: Sử dụng module csv

import csv
from io import StringIO

data = [['Name', 'Age', 'Country'], ['John', '25', 'USA'], ...]
csv_content = StringIO()
csv_writer = csv.writer(csv_content)
csv_writer.writerows(data)
csv_content.seek(0)
csv_string = csv_content.getvalue()


Ví dụ 4: Nối chuỗi quá mức trong việc xây dựng truy vấn SQL

query = "SELECT * FROM users WHERE"
filters = {'age': 25, 'country': 'USA', ...}
for field, value in filters.items():
    query += f" {field}='{value}' AND"
query = query.rstrip(' AND')


Giải pháp 4: Sử dụng truy vấn có tham số

import sqlite3

query = "SELECT * FROM users WHERE"
filters = {'age': 25, 'country': 'USA', ...}
placeholders = " AND ".join(f"{field} = ?" for field in filters)
values = tuple(filters.values())
full_query = f"{query} {placeholders}"
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
result = cursor.execute(full_query, values).fetchall()


Vấn đề: Đọc file không hiệu quả

Đọc file không hiệu quả đề cập đến các thực tiễn không tối ưu khi đọc file có thể dẫn đến hiệu năng kém và sử dụng tài nguyên không hiệu quả. Điều này có thể bao gồm các vấn đề như đọc file từng dòng bằng vòng lặp, thực hiện các thao tác I/O quá mức hoặc đọc toàn bộ file vào bộ nhớ không cần thiết. 

Hãy xem xét ví dụ bên dưới

lines = []
with open('data.txt', 'r') as file:
    for line in file:
        lines.append(line)


Giải pháp: Sử dụng lặp file

with open('data.txt', 'r') as file:
    lines = list(file)


Ưu điểm

  • Lặp qua đối tượng file trực tiếp tránh tiêu thụ bộ nhớ không cần thiết.
  • Nó cải thiện hiệu năng bằng cách đọc file từng phần.

Nhược điểm

  • Lặp file có thể không phù hợp nếu bạn cần truy cập ngẫu nhiên vào các dòng hoặc thực hiện các thao tác phức tạp trên file.

Một vài ví dụ khác được liệt kê bên dưới

Ví dụ 2: Đọc file không hiệu quả với các thao tác I/O quá mức

file_path = 'data.txt'
with open(file_path, 'r') as file:
    lines = file.readlines()
    for line in lines:
        # Thực hiện nhiều thao tác I/O cho mỗi dòng
        # ...


Giải pháp 2: Giảm thiểu các thao tác I/O

file_path = 'data.txt'
with open(file_path, 'r') as file:
    lines = file.readlines()

# Xử lý dữ liệu bên ngoài ngữ cảnh file
for line in lines:
    # Xử lý mỗi dòng
    # ...


Ví dụ 3: Đọc file không hiệu quả bằng cách đọc toàn bộ file vào bộ nhớ không cần thiết

file_path = 'large_data.txt'
with open(file_path, 'r') as file:
    file_content = file.read()
    # Xử lý toàn bộ nội dung file


Giải pháp 3: Sử dụng lặp file hoặc đọc từng đoạn

file_path = 'large_data.txt'
with open(file_path, 'r') as file:
    for line in file:
        # Xử lý từng dòng tăng dần
        # ...

# hoặc
with open(file_path, 'r') as file:
    chunk_size = 4096  # Điều chỉnh kích thước đoạn theo yêu cầu của bạn
    while True:
        chunk = file.read(chunk_size)
        if not chunk:
            break
        # Xử lý từng đoạn
        # ...

 

Vấn đề: Biểu thức chính quy tốn kém

 

Biểu thức chính quy tốn kém trong Python đề cập đến việc sử dụng biểu thức chính quy không hiệu quả có thể dẫn đến hiệu năng kém và tiêu thụ tài nguyên quá mức. Điều này có thể xảy ra do khớp mẫu không hiệu quả, quay lui quá mức hoặc biên dịch biểu thức chính quy không cần thiết. Trong phần này, tôi sẽ thảo luận về các vấn đề liên quan đến biểu thức chính quy tốn kém, cung cấp các ví dụ để minh họa vấn đề và đề xuất các giải pháp với các đoạn mã.

Hãy xem xét ví dụ bên dưới

import re

data = ['apple', 'banana', 'cherry']
results = []
for item in data:
    if re.match(r'a', item):
        results.append(item)


Giải pháp: Biên dịch trước biểu thức chính quy

import re
pattern = re.compile(r'a')
data = ['apple', 'banana', 'cherry']
results = [item for item in data if pattern.match(item)]


Ưu điểm

  • Biên dịch trước biểu thức chính quy cải thiện hiệu năng bằng cách tránh việc biên dịch dư thừa trong mỗi lần lặp.
  • Nó cung cấp sự tăng tốc đáng kể khi sử dụng cùng một mẫu nhiều lần.

Nhược điểm

  • Biên dịch trước biểu thức chính quy có thể thêm một số chi phí ban đầu nếu mẫu được sử dụng không thường xuyên hoặc thay đổi động.

Một vài ví dụ khác được liệt kê bên dưới

Ví dụ 2: Biểu thức chính quy tốn kém với các nhóm bắt giữ không cần thiết

import re
text = "Hello, world!"
pattern = "(Hello), (world)!"
match = re.match(pattern, text)


Giải pháp 2: Sử dụng các nhóm không bắt giữ hoặc xóa các nhóm bắt giữ

pattern = r"Hello, (?:world)!"
match = re.match(pattern, text)


Ví dụ 3: Biểu thức chính quy tốn kém với việc biên dịch dư thừa

import re
text = "The quick brown fox jumps over the lazy dog"
pattern = r"fox"
for _ in range(1000):
    match = re.match(pattern, text)


Giải pháp 3: Biên dịch biểu thức chính quy một lần và sử dụng lại

import re
text = "The quick brown fox jumps over the lazy dog"
pattern = re.compile(r"fox")
for _ in range(1000):
    match = pattern.match(text)

 

Kết luận

 

Tối ưu hóa hiệu năng Python là rất quan trọng để đạt được việc thực thi mã nhanh hơn và hiệu quả hơn. Bằng cách giải quyết các vấn đề phổ biến như vòng lặp không hiệu quả, nối chuỗi quá mức, đọc file không hiệu quả và biểu thức chính quy tốn kém, bạn có thể cải thiện đáng kể hiệu năng của các ứng dụng Python. Tuy nhiên, điều quan trọng là phải xem xét ưu điểm và nhược điểm của mỗi giải pháp để đảm bảo chúng phù hợp với trường hợp sử dụng cụ thể của bạn. 
Hãy nhớ rằng, tối ưu hóa hiệu năng nên luôn được cân bằng với khả năng đọc và khả năng bảo trì mã.
Trở thành bậc thầy về Tối ưu hóa Hiệu năng Python là điều cần thiết đối với mọi nhà phát triển Python cấp cao.

Tag list:
- Python
- Python Developer
- List Comprehension
- performance
- Python For Beginners
- File
- Optimization
- Regular Expression
- String Concatenation
- File Operation

Liên quan

Python

Đọc thêm
Python

Đọc thêm

Theo dõi

Theo dõi bản tin của chúng tôi và không bao giờ bỏ lỡ những tin tức mới nhất.