Tin tức và phân tích của tất cả các thiết bị di động

Zen của Python có thể giúp bạn viết mã tốt hơn như thế nào

Bạn muốn viết mã Python tốt hơn? Đây là cách Zen của Python có thể giúp bạn thực hiện những bước đầu tiên theo hướng đó.

Python rất dễ học. Nhưng việc viết mã Python đơn giản và dễ bảo trì có thể là một thách thức, đặc biệt đối với những lập trình viên mới làm quen. PEP-20 đã giới thiệu “The Zen of Python”, một bài thơ của Tim Peters nêu bật tầm quan trọng của việc viết mã Python tuân theo các phương pháp hay nhất.

Để đọc Zen của Python, bạn có thể chạy REPL của Python và chạy:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Như bạn có thể thấy, hầu hết các câu cách ngôn trong Zen của Python đều hiển nhiên. Khi giải thích, một số câu cách ngôn nên được kết hợp với những câu cách ngôn sau, trong khi những câu cách ngôn khác lại mâu thuẫn với câu cách ngôn trước đó. Tuy nhiên, Zen of Python rất thú vị, hấp dẫn và thực tế!

Giải thích Zen của Python

Zen của Python đã được đề xuất có 20 nguyên tắc hướng dẫn lập trình Python. Tuy nhiên, cho đến nay chỉ có 19 câu cách ngôn. Chúng ta hãy đi qua chúng.

Đẹp thì tốt hơn là xấu.

Câu cách ngôn này nhấn mạnh tầm quan trọng của việc viết mã Pythonic và thanh lịch.

Đoạn mã bên dưới có mùi mã:

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

Chức năng:

  • Khởi tạo một danh sách trống
  • Nó có một vòng lặp bên trong một hàm nối thêm các mục vào cuối danh sách.
  • Cuối cùng, nó trả về một danh sách

Mặc dù điều này đúng về mặt chức năng – nhưng nó không phải là Pythonic – và rất khó để duy trì.

Bạn có thể viết nó thanh lịch hơn nhiều bằng cách sử dụng máy phát điện. Đây là hàm tạo tương đương của hàm trên:

def square(num):
    for i in range(num):
        yield i*i

Hoặc tốt hơn nữa, bạn có thể có biểu thức tạo khả năng hiểu sau:

num = ...
squares = (i*i for i in range(num))

Công khai thì tốt hơn là ẩn giấu.

Khi viết mã, đừng để các nhà phát triển và người dùng khác đoán hành vi mặc định hoặc ngụ ý của mã. Hãy rõ ràng. Hãy lấy một ví dụ về việc nhập ký tự đại diện:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

Tránh sử dụng ký tự đại diện càng nhiều càng tốt. Bởi vì nó không rõ ràng và không hiệu quả. Hãy cụ thể khi nhập các hàm và lớp từ các mô-đun khác:

from some_module import this_function # explicit import

result = this_function() # we now know.

Đơn giản là tốt hơn phức tạp.

Câu cách ngôn này nói rằng chúng ta nên giữ mã đơn giản và tránh sự phức tạp không cần thiết. Ví dụ: bạn có thể muốn đảo ngược chuỗi và triển khai giải pháp đệ quy sau:

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

Mặc dù cách này có hiệu quả nhưng đây có thể là một giải pháp quá mức cần thiết cho vấn đề – vì có nhiều cách đơn giản hơn và mang tính Pythonic hơn để thực hiện điều đó.

Đây là một cách tiếp cận cắt chuỗi:

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

Đây là một cách tiếp cận sử dụng các phương thức và hàm dựng sẵn:

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

Phức tạp thì tốt hơn phức tạp.

Vậy câu cách ngôn tiếp theo trong Zen của Python truyền tải điều gì?

Đảo ngược một chuỗi trong Python là một thao tác rất đơn giản. Tuy nhiên, trong thực tế, chúng ta có thể cần logic phức tạp hơn. Đây là một ví dụ khá đơn giản:

Giả sử bạn cần kết nối với cơ sở dữ liệu:

  • Trước tiên, bạn cần phân tích tệp cấu hình toml để lấy thông tin cấu hình cơ sở dữ liệu.
  • Trình kết nối cơ sở dữ liệu nên được cài đặt.
  • Sau đó, bạn có thể xác định một hàm sẽ kết nối với cơ sở dữ liệu, dự đoán lỗi kết nối, triển khai xử lý lỗi, v.v.
  • Cuối cùng, sau khi kết nối với cơ sở dữ liệu, bạn có thể truy vấn nó.

Mặc dù việc này vẫn khá đơn giản nhưng nó đòi hỏi logic phức tạp hơn so với việc đảo ngược chuỗi. Nhưng điều đó không có nghĩa là nó phải phức tạp. Bạn vẫn có thể sử dụng hiệu quả các tính năng mã tích hợp của mô-đun và sắp xếp mã của mình để các nhà phát triển khác có thể đọc, hiểu và đóng góp cho mã đó.

Flat tốt hơn lồng nhau.

Cấu trúc phẳng dễ phân tích và hiểu hơn cấu trúc lồng nhau. Khi làm việc trên một dự án, bạn có thể muốn tách biệt chức năng bằng cách tạo các mô-đun riêng biệt. Tuy nhiên, quá nhiều ngũ cốc có thể là quá mức.

Điều đó nói lên rằng, đôi khi cần phải vượt ra ngoài cấu trúc phẳng. Nhưng ngay cả khi bạn cần làm tổ, hãy giữ nó ở mức tối thiểu.

Đây là một ví dụ:

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

Mỏng thì tốt hơn dày.

Nếu bạn là người mới làm quen với lập trình, bạn có thể muốn lạm dụng một số tính năng của ngôn ngữ. Ví dụ: khả năng hiểu danh sách bằng Python – nhưng chỉ khi bạn sử dụng chúng khi bạn cần.

Hãy xem xét lý do sau:

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Output: [('melons', 40)]

Danh sách kết hợp quá dày đặc và khó phân tích. Trong trường hợp này, việc sử dụng vòng lặp for tương đương với các biểu thức điều kiện sẽ dễ đọc hơn. Ý nghĩa của sự hiểu biết là khó hiểu. 🙂

Vấn đề dễ đọc.

Bạn phải luôn viết mã có thể đọc được. Dưới đây là một số cách đơn giản để cải thiện khả năng đọc mã:

  • Sử dụng tên biến mô tả
  • Thêm tài liệu cho các hàm và lớp
  • Nhận xét về mã khi cần thiết
  • Thêm gợi ý kiểu cho đối số hàm và kiểu trả về

Trường hợp đặc biệt không đủ đặc biệt để phá vỡ các quy tắc.

Tuân theo các quy tắc ngôn ngữ và các phương pháp hay nhất được đề xuất bất cứ khi nào có thể.

Nhưng nó luôn luôn có thể? Không, đó là lý do tại sao chúng ta có một câu cách ngôn khác.

Mặc dù tính thực tế chiến thắng sự sạch sẽ.

Đây là sự tiếp nối của câu cách ngôn trước đó. Mặc dù nên tuân theo các quy tắc của ngôn ngữ, nhưng trong một số trường hợp, việc không tuân theo các quy tắc nhất định là hoàn toàn bình thường.

Lỗi không bao giờ nên diễn ra âm thầm.

Lỗi thời gian chạy khá phổ biến trong Python. Một cách thực hành tốt là bạn nên luôn xử lý lỗi và không nên tắt tiếng chúng như một cách khắc phục nhanh chóng.

Bạn có thể dự đoán và thực hiện xử lý lỗi thích hợp cho các loại lỗi khác nhau:

try:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

Bạn nên tránh các trường hợp ngoại lệ trần trụi và chung chung. Các phiên bản mới hơn của Python (từ python 3.11) xử lý các chuỗi ngoại lệ và nhóm ngoại lệ để thực hiện xử lý ngoại lệ phức tạp hơn.

Trừ khi anh ta rõ ràng im lặng.

Điều này tiếp nối câu cách ngôn trước đó. Nếu thiết kế của bạn yêu cầu hoặc cho phép bạn ngăn chặn lỗi, hãy thực hiện điều đó một cách rõ ràng.

Ví dụ: khi kết nối với cơ sở dữ liệu, bạn có thể gặp lỗi vận hành do thông tin cấu hình không chính xác. Hãy thử kết nối bằng cấu hình tùy chỉnh. Nếu xảy ra lỗi vận hành, hãy sử dụng cấu hình mặc định và thử kết nối với cơ sở dữ liệu.

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

Khi đối mặt với sự mơ hồ, hãy chống lại sự cám dỗ để đoán.

Câu cách ngôn này trong Zen của Python là hiển nhiên. Khi nghi ngờ, đừng đoán. Nhưng hãy chạy mã và kiểm tra đầu ra. Sau đó, tùy thuộc vào việc bạn có hành vi mong muốn hay không, hãy cải thiện khả năng đọc hoặc sửa đổi logic nếu cần.

Hãy lấy ví dụ đơn giản sau đây với bộ dữ liệu Boolean:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

Nên có một – và tốt nhất là chỉ một – cách rõ ràng để thực hiện việc này.

Để hoàn thành một nhiệm vụ cụ thể, cần có một và chỉ một cách Python được đề xuất để thực hiện việc đó. Tuy nhiên, đối với bất kỳ vấn đề nào, chúng ta có thể có nhiều giải pháp.

Ngay cả trong ví dụ đảo ngược chuỗi đơn giản, chúng ta đã xem xét giải pháp đệ quy, tách chuỗi và phương thức join().

Đây cũng là một trò đùa nội tâm do việc sử dụng dấu gạch ngang không nhất quán. Chúng ta thường sử dụng dấu gạch ngang mà không có dấu cách ở đầu và cuối. Hoặc chúng ta sử dụng nó với cả dấu cách ở đầu và cuối.

Đây là những gì chúng ta có thể suy luận. Câu cách ngôn nhấn mạnh rằng nên có một – và chỉ một – cách làm việc của Python có thể được viết bằng nhiều hơn hai cách.

Mặc dù lúc đầu cách này có thể không rõ ràng trừ khi bạn là người Hà Lan.

Nói một cách nhẹ nhàng, nó đề cập đến Guido Van Rossum, người tạo ra Python (người Hà Lan). Cách (nhất) Pythonic để thực hiện một nhiệm vụ nhất định – nó chỉ đến với những người tạo ra Python một cách tự nhiên.

Vì vậy các lập trình viên cần có kinh nghiệm – và rút kinh nghiệm – để sử dụng tốt hơn các tính năng của ngôn ngữ.

Bây giờ thì tốt hơn là không bao giờ.

Giống như một số câu cách ngôn khác trong Python’s Zen, câu cách ngôn này có thể được diễn giải theo nhiều cách khác nhau.

Một cách giải thích là với tư cách là một nhà phát triển, bạn khá thường xuyên trì hoãn trước khi bắt đầu viết mã cho một dự án. Thay vì chờ đợi lên kế hoạch cho những chi tiết nhỏ nhất cho dự án của bạn, tốt hơn hết bạn nên bắt đầu ngay bây giờ.

Một cách giải thích khác có thể là mã chạy với số bước hữu hạn – và kết thúc – thường tốt hơn mã có lỗi và bị mắc kẹt trong một vòng lặp vô hạn.

Mặc dù không bao giờ thường tốt hơn bây giờ.

Câu cách ngôn này có vẻ mâu thuẫn với câu trước. Mặc dù tốt hơn hết là đừng trì hoãn nhưng cũng đáng để suy nghĩ về vấn đề và thiết kế mã cho phù hợp.

Sẽ là một ý tưởng tồi khi viết mã một mô-đun – nếu không có suy nghĩ đúng đắn – chứa đầy mùi mã và phản mẫu. Bởi vì mã như vậy khó có thể cấu trúc lại và thực hiện các bản sửa lỗi.

Nếu việc triển khai khó giải thích thì đó là một ý tưởng tồi.

Bất kỳ logic nào, dù phức tạp đến đâu, luôn có thể được triển khai ở dạng dễ giải thích và dễ hiểu.

Nếu việc triển khai khó giải thích thì có lẽ đó là sự phức tạp không cần thiết. Mã có thể được sửa đổi hoặc tái cấu trúc để dễ theo dõi hơn.

Nếu việc triển khai dễ giải thích thì đó có thể là một ý tưởng hay.

Điều này có liên quan đến câu cách ngôn trước đó và cũng là điều hiển nhiên. Nếu việc triển khai có thể được giải thích một cách đơn giản thì đó có lẽ là một ý tưởng hay.

Bởi vì mã có thể được triển khai bằng các thuật ngữ đơn giản thường dễ đọc và dễ làm theo với độ phức tạp tối thiểu.

Không gian tên là một ý tưởng tuyệt vời – hãy làm nhiều hơn nữa!

Trong Python, các đối tượng trong một phạm vi cụ thể có thể được truy cập bằng tên của chúng trong không gian tên của chúng. Ví dụ: bạn có thể tạo một lớp và sử dụng nó làm mẫu để khởi tạo lớp đó. Bây giờ tất cả các biến thể hiện sẽ nằm trong không gian tên thể hiện.

Điều này cho phép chúng ta sử dụng các đối tượng có cùng tên – không có xung đột – vì chúng nằm trong các không gian tên khác nhau. Tuy nhiên, bạn chỉ nên sử dụng chúng khi cần thiết và đảm bảo rằng tính đơn giản cũng như khả năng đọc mã của bạn không bị ảnh hưởng.

Ứng dụng

Đó là tất cả trong hướng dẫn này! Tôi hy vọng hướng dẫn này đã giúp bạn hiểu cách Zen của Python nhấn mạnh kiểu mã và các phương pháp viết mã tốt trong Python. Bạn càng viết mã nhiều, bạn càng làm tốt hơn.

Nếu bạn muốn tìm hiểu cách viết mã ngắn gọn và dễ đọc, hãy đọc bài viết này về văn bản Python một dòng.