Với hướng dẫn cho người mới bắt đầu này, bạn sẽ bắt đầu khám phá PEP-8, hướng dẫn phong cách của Python, để định dạng mã đúng cách nhằm tối đa hóa khả năng đọc!
PEP-8 hay Python Enhancement Proposal trình bày một số điểm mấu chốt giúp bạn viết mã có tổ chức và dễ đọc hơn. Như nhà sáng tạo Python Guido Van Rossum nói:
Mã chương trình được đọc nhiều hơn rất nhiều so với được viết.
Trong bài viết này, bạn sẽ bắt đầu khám phá PEP-8 với một số ví dụ mã! Bạn sẽ đề cập đến các chủ đề sau:
Đầu tiên, bạn sẽ được giới thiệu PEP-8, nó là gì và vì sao bạn cần nó;
Tiếp theo, bạn sẽ xử lý thụt lề, một chủ đề nóng trong giới lập trình. Nên dùng tab hay khoảng trắng? Bạn sẽ tìm thấy câu trả lời trong phần này;
Có thể bạn không ngờ tới, nhưng cũng có hướng dẫn về độ dài dòng tối đa;
Ngoài ra, có một cách được đề xuất để xử lý dòng trống;
Tiếp theo, khoảng trắng trong biểu thức và câu lệnh là thứ mà bạn cũng có thể dễ dàng nắm được khi mới bắt đầu;
Mã hóa (encoding) là gì và vì sao bạn cần nó khi làm việc với Python? Mã hóa mặc định cho Python3 là gì? Phần mã hóa dòng nguồn sẽ giải quyết tất cả những điều này.
Bạn có lẽ thường xuyên import khi viết mã. Phần này sẽ bàn về thứ tự import, import tuyệt đối và tương đối, import wildcard, v.v.;
Tài liệu là thiết yếu để theo dõi mọi khía cạnh của một ứng dụng và cải thiện chất lượng sản phẩm cuối. Bình luận (comments) là cốt lõi ở đây!
Bạn có biết các tên dunder cấp mô-đun không? Chúng đặc biệt hữu ích trong docstring!
Cuối cùng, bạn cũng sẽ học thêm về quy ước đặt tên: cách nghĩ ra tên hàm, kiểu phong cách đặt tên thường dùng, và nhiều hơn nữa;
Mã của bạn đã tuân thủ PEP-8 chưa? Chắc chắn là câu hỏi bạn nên tự hỏi. Đó là lý do phần cuối cùng đề cập đến một số công cụ giúp bạn kiểm tra mã xem có tuân thủ các hướng dẫn trình bày trong bài viết này và nhiều nội dung khác mà ta chưa đề cập ở đây không!
Giới thiệu về PEP-8
Ngôn ngữ lập trình Python đã phát triển thành một trong những lựa chọn ưa thích của nhiều người. Đây là ngôn ngữ tương đối dễ học, đa mô hình, có vô số mô-đun mã nguồn mở gia tăng tiện ích, và là công cụ ưa dùng trong cộng đồng khoa học dữ liệu và phát triển web.
Tuy nhiên, bạn chỉ tận dụng được lợi ích của Python khi biết diễn đạt ý tưởng tốt hơn bằng mã. Python được tạo ra với một số mục tiêu nhất định, bạn có thể thấy các mục tiêu này khi gõ 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!Được hỗ trợ bởi
Trên đây là 20 nguyên lý mà lập trình Python sử dụng, được gọi là Thiền Python (Zen of Python). Bạn cũng thấy "Readability Counts" trong đầu ra ở trên, điều này nên là mối quan tâm chính khi viết mã: các lập trình viên hoặc nhà khoa học dữ liệu khác phải hiểu và có thể đóng góp vào mã của bạn để giải quyết nhiệm vụ đặt ra.
Các phần tiếp theo sẽ cho bạn thêm hiểu biết về cách đạt được điều đó!
Thụt lề
Khi lập trình bằng Python, thụt lề là thứ bạn chắc chắn sẽ dùng. Tuy nhiên, bạn nên cẩn thận vì nó có thể dẫn tới lỗi cú pháp. Khuyến nghị là dùng bốn khoảng trắng để thụt lề. Ví dụ, câu lệnh này dùng bốn khoảng trắng để thụt lề:
dict_of_people_ages ={"ram":25,"john":29,"shiva":26}Được hỗ trợ bởi
Mỗi lập trình viên làm việc với Python hay ngôn ngữ khác đều tự hỏi liệu nên dùng tab hay khoảng trắng để thụt lề. Sự khác nhau giữa tab và khoảng trắng là cuộc tranh luận kéo dài trong cộng đồng. Hãy xem, chẳng hạn, bài viết trên Stackoverflow này.
Nhìn chung, khoảng trắng là cách thụt lề được ưa chuộng, nhưng nếu bạn thấy một số script Python đã dùng tab, bạn nên tiếp tục thụt lề bằng tab. Nếu không, bạn nên đổi thụt lề của mọi biểu thức trong script sang dùng khoảng trắng.
Lưu ý rằng Python 3 không cho phép trộn tab và khoảng trắng để thụt lề. Vì vậy bạn nên chọn một trong hai và kiên định với nó!
Độ dài dòng tối đa
Nhìn chung, nên đặt mục tiêu độ dài dòng là 79 ký tự trong mã Python của bạn.
Tuân theo con số mục tiêu này có nhiều lợi ích. Một vài trong số đó là:
Có thể mở tệp cạnh nhau để so sánh;
Bạn có thể xem toàn bộ biểu thức mà không cần cuộn ngang, giúp tăng khả năng đọc và hiểu mã.
Comment nên có độ dài dòng 72 ký tự. Bạn sẽ học thêm về các quy ước phổ biến cho comment ở phần sau của hướng dẫn này!
Cuối cùng, nếu bạn làm việc trong nhóm nhỏ, việc chọn theo quy ước và phong cách nào là tùy bạn, và đa số lập trình viên chấp nhận việc đôi khi lệch khỏi hướng dẫn độ dài dòng tối đa. Tuy nhiên, nếu bạn tạo hoặc đóng góp cho dự án mã nguồn mở, bạn có lẽ sẽ muốn và/hoặc cần tuân thủ quy tắc độ dài dòng tối đa được PEP-8 đặt ra.
Khi dùng toán tử +, tốt hơn là dùng xuống dòng hợp lý, giúp mã dễ hiểu hơn:
Tóm lại, bạn có thể xuống dòng trước hoặc sau toán tử nhị phân, miễn là nhất quán. Nếu bạn đang viết mã mới, hãy cố gắng theo lựa chọn cuối cùng đã trình bày, nơi bạn xuống dòng trước toán tử nhị phân.
Dòng trống
Trong các script Python, các hàm và lớp cấp cao (top-level) được ngăn cách bởi hai dòng trống. Định nghĩa phương thức bên trong lớp nên ngăn cách nhau bởi một dòng trống. Bạn có thể thấy rõ trong ví dụ sau:
import unittest
classSwapTestSuite(unittest.TestCase):"""
Swap Operation Test Case
"""defsetUp(self):
self.a =1
self.b =2deftest_swap_operations(self):
instance = Swap(self.a,self.b)
value1, value2 =instance.get_swap_values()
self.assertEqual(self.a, value2)
self.assertEqual(self.b, value1)classOddOrEvenTestSuite(unittest.TestCase):"""
This is the Odd or Even Test case Suite
"""defsetUp(self):
self.value1 =1
self.value2 =2deftest_odd_even_operations(self):
instance1 = OddOrEven(self.value1)
instance2 = OddOrEven(self.value2)
message1 = instance1.get_odd_or_even()
message2 = instance2.get_odd_or_even()
self.assertEqual(message1,'Odd')
self.assertEqual(message2,'Even')Được hỗ trợ bởi
Các lớp SwapTestSuite và OddOrEvenTestSuite được ngăn cách bởi hai dòng trống, trong khi các định nghĩa phương thức như .setUp() và .test_swap_operations() chỉ có một dòng trống để ngăn cách. Mã sẽ chạy thành công, nhưng sẽ không tạo ra đầu ra nào vì thiếu mã cần thiết để chạy unit test (chỉ minh họa thực hành tốt).
Khoảng trắng trong biểu thức và câu lệnh
Bạn nên tránh khoảng trắng khi thấy mã của mình viết như trong các ví dụ sau:
Một máy tính không thể lưu trữ "chữ", "số", "hình ảnh" hay bất kỳ thứ gì khác; Nó chỉ có thể lưu trữ và làm việc với bit, vốn chỉ có giá trị nhị phân: yes hoặc no, true hoặc false, 1 hoặc 0, v.v. Như bạn đã biết, máy tính hoạt động bằng điện; Điều này có nghĩa là một bit "thực" là sự có mặt hay vắng mặt của một xung điện. Thông thường bạn biểu diễn sự (không) có mặt này bằng 1 và 0.
Để dùng bit biểu diễn bất cứ thứ gì khác ngoài chính bit, bạn cần một bộ quy tắc. Bạn cần chuyển một chuỗi bit thành thứ như chữ cái, con số và hình ảnh bằng một lược đồ mã hóa hay encoding. Ví dụ về lược đồ mã hóa là ASCII, UTF-8, v.v.:
American Standard Code for Information Interchange (ASCII) là định dạng phổ biến nhất cho tệp văn bản trên máy tính và Internet. Trong kiểu tệp này, mỗi ký tự chữ, số, hoặc ký tự đặc biệt được biểu diễn bằng một số nhị phân 7-bit (một chuỗi bảy số 0 hoặc 1).
Unicode Worldwide Character Standard, hay ngắn gọn là Unicode, là hệ thống dành cho "việc trao đổi, xử lý và hiển thị văn bản viết của các ngôn ngữ đa dạng trên thế giới hiện đại". Nói ngắn gọn, Unicode được thiết kế để bao quát mọi hệ chữ viết đã biết trên thế giới. Unicode hiện sử dụng ba kiểu mã hóa khác nhau để biểu diễn các bộ ký tự Unicode: UTF-8, UTF-16 và UTF-32.
UTF-16 là mã hóa Unicode có độ dài biến đổi: các điểm mã được mã hóa bằng một hoặc hai đơn vị mã 16-bit.
UTF-8 là một kiểu mã hóa Unicode có độ dài biến đổi khác, dùng từ một đến bốn byte 8-bit.
UTF-32 là mã hóa độ dài cố định, dùng chính xác 32 bit cho mỗi điểm mã Unicode.
Mẹo: nếu bạn muốn biết thêm về mã hóa, hãy xem bài viết này.
Vậy điều này quan trọng vì sao?
Bạn sẽ nhận ra rằng chuỗi (string) là một trong những kiểu dữ liệu được dùng nhiều nhất trong Python. Như bạn có thể đoán, sẽ có lúc bạn muốn làm việc với chuỗi có chứa hoặc hoàn toàn gồm các ký tự không thuộc bộ ASCII chuẩn. Suy cho cùng, có thể bạn phải làm việc với văn bản chứa ký tự có dấu, như á, ž, ç v.v.
Trong Python 3, UTF-8 là mã hóa nguồn mặc định. Nhưng với những ai dùng Python 2, bạn có lẽ đã biết mặc định khi đó là ASCII.
Vậy nếu bạn có một chuỗi chứa ký tự không phải ASCII, như "Flügel" thì sao?
Khi bạn tham chiếu chuỗi trong Python 2, bạn sẽ nhận được như sau:
In ra cho bạn giá trị đã gán cho biến. Ký tự không thuộc ASCII ÃŒ đã được mã hóa. Đó là lý do bạn nhận về \xfc khi tham chiếu chuỗi. Để xử lý việc này, bạn có thể dùng các phương thức chuỗi .encode() và .decode(). Cái trước trả về phiên bản chuỗi 8-bit của chuỗi Unicode, được mã hóa theo encoding yêu cầu, trong khi cái sau diễn giải chuỗi theo encoding đã cho.
Import
Import thư viện và/hoặc mô-đun là việc bạn sẽ thường làm khi làm việc với Python cho khoa học dữ liệu. Như bạn có thể đã biết, bạn nên luôn import thư viện ở đầu script.
Lưu ý rằng nếu bạn có nhiều import, hãy đảm bảo mỗi import ở trên một dòng riêng.
Xem bảng sau để hiểu rõ hơn:
Bạn nên dùng...
Bạn nên tránh...
from config import settings
hoặc
import os import sys
import os, sys
Thêm nữa, bạn cần lưu ý có một thứ tự cần tuân theo khi import thư viện. Nói chung, bạn có thể theo thứ tự này:
Import thư viện chuẩn (standard library).
Import bên thứ ba liên quan.
Import dành riêng cho ứng dụng/thư viện cục bộ.
Import tuyệt đối và tương đối
Tiếp theo, tốt để biết sự khác nhau giữa import tuyệt đối và tương đối. Nói chung, import tuyệt đối được ưu tiên trong Python vì tăng tính dễ đọc. Tuy nhiên, khi ứng dụng của bạn trở nên phức tạp hơn, bạn cũng có thể dùng import tương đối. Import tương đối ngầm định không bao giờ nên dùng và đã bị loại bỏ trong Python 3.
Vậy import tuyệt đối và tương đối là gì?
Import tuyệt đối là import sử dụng đường dẫn tuyệt đối của hàm hoặc lớp, phân tách bằng .. Ví dụ,
import sklearn.linear_model.LogisticRegression
Được hỗ trợ bởi
Import tương đối là import tương ứng với vị trí hiện tại nơi tệp Python của bạn tồn tại. Bạn có thể dùng kiểu import này nếu cấu trúc dự án của bạn lớn dần, vì nó sẽ làm dự án dễ đọc hơn. Nghĩa là, nếu bạn có cấu trúc dự án Python như sau:
Nếu muốn biết thêm về import tuyệt đối và tương đối, bạn có thể xem PEP 328.
Import wildcard
Cuối cùng, bạn nên tránh import wildcard, vì chúng không giúp tăng khả năng đọc; Bạn sẽ không biết rõ đang dùng những lớp, phương thức hay biến nào từ mô-đun của mình, ví dụ:
Comment được dùng cho tài liệu nội tuyến trong Python. Chúng giúp hiểu mã tốt hơn. Có nhiều công cụ bạn có thể dùng để tạo tài liệu, như comments và docstrings, cho mô-đun của chính bạn. Comment nên chi tiết hơn để khi ai đó đọc mã, họ có thể hiểu đúng về mã và cách nó được dùng với các phần khác của mã.
Comment bắt đầu với ký hiệu #. Bất cứ điều gì viết sau dấu thăng sẽ không được thông dịch viên thực thi. Ví dụ, đoạn mã sau sẽ chỉ in ra "This is a Python comment".
# This is a Python single line commentprint("This is a Python comment")Được hỗ trợ bởi
Hãy nhớ: ở phần trước, bạn đã đọc rằng comment nên có độ dài dòng 72 ký tự!
Như vậy, có ba loại comment:
Bạn dùng block comment để giải thích mã phức tạp hơn hoặc lạ lẫm với người khác. Chúng thường là comment dạng dài và áp dụng cho một phần hoặc toàn bộ mã theo sau. Block comment được thụt lề cùng mức với mã. Mỗi dòng của block comment bắt đầu bằng dấu thăng # và một khoảng trắng. Nếu cần nhiều hơn một đoạn, chúng nên được tách bằng một dòng chỉ chứa một #.
if Gram isNoneor Gram isFalse:
Gram =Noneif copy_X:# force copy. setting the array to be fortran-ordered# speeds up the calculation of the (partial) Gram matrix# and allows to easily swap columns
X = X.copy('F')Được hỗ trợ bởi
Bạn nên dùng inline comment một cách tiết chế, dù chúng hiệu quả khi bạn cần giải thích một số phần mã. Chúng cũng có thể giúp bạn nhớ một dòng mã cụ thể nghĩa là gì hoặc hữu ích khi bạn cộng tác với ai đó chưa quen thuộc toàn bộ mã của bạn. Bạn dùng inline comment trên cùng một dòng với câu lệnh, theo sau chính đoạn mã đó. Những comment này cũng bắt đầu với # và một khoảng trắng.
Bạn viết chuỗi tài liệu hay docstring ở đầu các mô-đun, tệp, lớp và phương thức công khai. Kiểu comment này bắt đầu với """ và kết thúc với """:
"""
This module is intended to provide functions for scientific computing
"""Được hỗ trợ bởi
Tên dunder cấp mô-đun
Giờ bạn đã đọc docstring là gì, bạn cũng nên biết về các dunder cấp mô-đun, tức các tên có hai gạch dưới ở đầu và cuối, rất hữu hiệu trong Python. Đây là các tên đặc biệt mà Python định nghĩa để không xung đột với các hàm hay tên do người dùng định nghĩa. Để biết thêm, bạn có thể xem bài viết này.
Một dunder cấp mô-đun như (__all__, __author__, __version__) nên được đặt sau docstring chính của mô-đun và trước tất cả câu lệnh import. Bạn nên định nghĩa các import from __future__ trước bất kỳ mã nào khác, ngoại trừ docstring:
"""
Algos module consists of all the basic algorithms and their implementation
"""from __future__ importprint
__all__ =['searching','sorting']
__version__ ='0.0.1'
__author__ ='Chitrank Dixit'import os
import sys
Được hỗ trợ bởi
Khi lập trình bằng Python, bạn gần như chắc chắn sẽ sử dụng một quy ước đặt tên, tức tập hợp quy tắc để chọn chuỗi ký tự dùng cho định danh biểu thị biến, kiểu, hàm, và các thực thể khác trong mã nguồn và tài liệu.
Nếu bạn chưa rõ có những phong cách đặt tên nào, hãy cân nhắc các kiểu sau:
b hoặc một chữ thường đơn;
B hoặc một chữ hoa đơn;
lowercase
UPPERCASE
lower_case_with_underscores
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords, còn được biết đến là CapWords, CamelCase hoặc StudlyCaps.
mixedCase
Capitalized_Words_With_Underscores
_single_leading_underscore: chỉ báo yếu cho "dùng nội bộ". ví dụ, from M import * không import các đối tượng có tên bắt đầu bằng gạch dưới.
single_trailing_underscore_: dùng theo quy ước để tránh xung đột với từ khóa Python, ví dụ, Tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore: khi đặt tên thuộc tính lớp, sẽ kích hoạt name mangling (bên trong lớp FooBar, __boo trở thành _FooBar__boo).
__double_leading_and_trailing_underscore__: các đối tượng hoặc thuộc tính "ma thuật" tồn tại trong không gian tên do người dùng kiểm soát. Ví dụ, __init__, __import__ hoặc __file__. Bạn không nên tự nghĩ ra các tên như vậy, chỉ dùng theo tài liệu.
Quy ước đặt tên chung
Bảng sau cho bạn một số hướng dẫn chung về cách đặt tên định danh:
Định danh
Quy ước
Mô-đun
lowercase
Lớp
CapWords
Hàm
lowercase
Phương thức
lowercase
Biến kiểu (Type variables)
CapWords
Hằng số
UPPERCASE
Gói
lowercase
Không dùng 'l', 'O' hoặc 'I' làm tên biến đơn: các ký tự này trông giống số không (0) và (1) trong một số phông chữ.
Nói chung, nên dùng tên ngắn nếu có thể. Trong một số trường hợp, bạn có thể dùng gạch dưới để cải thiện khả năng đọc.
Nếu bạn muốn biết thêm về các ngoại lệ cho quy ước đặt tên chung, hãy xem bài viết này.
Mã của bạn có tuân thủ PEP-8 không?
Sau khi tìm hiểu thêm về PEP-8, có lẽ bạn đang tự hỏi làm sao kiểm tra mã của mình có thực sự tuân thủ các hướng dẫn này (và nhiều nội dung chưa được đề cập trong hướng dẫn này!) hay không.
Bên cạnh việc tự đọc PEP-8 và học thêm về nó, bạn chắc chắn nên cân nhắc tìm hiểu mô-đun tiện dụng pep8, gói coala và một số lựa chọn thay thế khác được mô tả trong các phần tiếp theo!
Gói Python pep8
Gói pep8 có thể được dùng để kiểm tra sự không tương thích với PEP-8 trong mã Python của bạn và đề xuất thay đổi để tuân theo các hướng dẫn PEP-8. Bạn có thể cài mô-đun pep8 bằng pip với lệnh sau:
Lệnh này sẽ xuất ra mọi điểm không tương thích PEP-8 mà pep8 phát hiện trong mã, như sau:
example.py:1:1: E302 expected 2 blank lines, found 0
example.py:3:5: E225 missing whitespace around operator
example.py:5:5: E225 missing whitespace around operatorĐược hỗ trợ bởi
Dòng đầu cho biết cần có hai dòng trống trước định nghĩa hàm ở dòng 1. Hai dòng tiếp cho biết cần có khoảng trắng quanh toán tử cộng trong câu lệnh print và các câu lệnh gán.
Để sửa các vấn đề này, bạn có thể chỉnh mã như sau:
defmy_function(a, b):print("The sum of a and b is:", a + b)
a =1
b =2
my_function(a, b)Được hỗ trợ bởi
Giờ, khi bạn chạy lại pep8 example.py, sẽ không có đầu ra nào, nghĩa là mã đã tuân thủ PEP-8.
Bạn cũng có thể xem mã nguồn nơi phát hiện không tương thích với tham số --show-source:
$ pep8 --show-source --show-pep8 testsuite/E40.py
testsuite/E40.py:2:10:E401 multiple imports on one line
import os, sys
^
Imports should usually be on separate lines.Okay:import os\nimport sys
E401:import sys, os
Được hỗ trợ bởi
Hoặc bạn có thể hiển thị tần suất từng lỗi được phát hiện bằng cách thêm --statistics:
$ pep8 --statistics -qq Python-2.5/Lib
232E201 whitespace after '['599E202 whitespace before ')'631E203 whitespace before ','842E211 whitespace before '('2531E221 multiple spaces before operator
4473E301 expected 1 blank line, found 04006E302 expected 2 blank lines, found 1165E303 too many blank lines(4)325E401 multiple imports on one line
3615E501 line too long(82 characters)612W601.has_key() is deprecated, use 'in'1188W602 deprecated form of raising exception
Được hỗ trợ bởi
Trong đoạn mã trên, bạn thấy thực ra bạn cài coala-bears: bears là các plugin hoặc mô-đun đơn giản mở rộng khả năng của coala và khác nhau theo ngôn ngữ. Trong trường hợp này, bạn muốn dùng pep8bear, tìm mã không tương thích PEP-8 và sửa trực tiếp. Bạn chắc chắn nên cân nhắc dùng công cụ này để kiểm tra mã Python của bạn.
Bên cạnh mô-đun pep8 tiện dụng và gói coala, bạn cũng có thể kiểm tra mã Python có tuân thủ PEP-8 hay không bằng cách truy cập pep8online. Trang này có trình soạn thảo trực tuyến cho phép bạn dán mã vào, nhấn nút "Check code"! Kết quả, bạn sẽ nhận được phản hồi về những gì cần cải thiện. Tiện lợi!
Kết luận
Khi dùng Python, đôi khi bạn không chú ý đến chất lượng mã vì áp lực phát hành tính năng nhanh. Tuy nhiên, các thực hành được mô tả trong hướng dẫn này -và nhiều điều nữa chưa được đề cập- nên là một phần của chu trình phát triển-dàn dựng-kiểm thử-triển khai của bạn. Điều này có lợi cho mọi người trong dự án để hiểu và phần lớn thời gian có thể thay đổi mã mà không cần đào sâu và hiểu mã bằng cách khởi động trình gỡ lỗi. Nếu bạn làm việc trên dự án mã nguồn mở, những người đóng góp sẽ thấy PEP-8 thật tuyệt và hiểu mã của bạn tốt hơn vì đây là tiêu chuẩn phổ quát, mỗi lập trình viên Python đều theo.
Giờ bạn đã đi qua hướng dẫn này, một ý tưởng rất hay là tự mình xem PEP-8! Còn rất nhiều điều để khám phá.
Nếu bạn có thêm mẹo nào để tuân thủ PEP-8 hoặc nghĩ rằng chúng tôi đã bỏ sót điều gì quan trọng trong bài viết, đừng ngần ngại cho chúng tôi biết tại @DataCamp.
PEP-8 hay Python Enhancement Proposal trình bày một số điểm mấu chốt giúp bạn viết mã có tổ chức và dễ đọc hơn. Như nhà sáng tạo Python Guido Van Rossum nói:
Trong bài viết này, bạn sẽ bắt đầu khám phá PEP-8 với một số ví dụ mã! Bạn sẽ đề cập đến các chủ đề sau:
Giới thiệu về PEP-8
Ngôn ngữ lập trình Python đã phát triển thành một trong những lựa chọn ưa thích của nhiều người. Đây là ngôn ngữ tương đối dễ học, đa mô hình, có vô số mô-đun mã nguồn mở gia tăng tiện ích, và là công cụ ưa dùng trong cộng đồng khoa học dữ liệu và phát triển web.
Tuy nhiên, bạn chỉ tận dụng được lợi ích của Python khi biết diễn đạt ý tưởng tốt hơn bằng mã. Python được tạo ra với một số mục tiêu nhất định, bạn có thể thấy các mục tiêu này khi gõ
import this.Trên đây là 20 nguyên lý mà lập trình Python sử dụng, được gọi là Thiền Python (Zen of Python). Bạn cũng thấy "Readability Counts" trong đầu ra ở trên, điều này nên là mối quan tâm chính khi viết mã: các lập trình viên hoặc nhà khoa học dữ liệu khác phải hiểu và có thể đóng góp vào mã của bạn để giải quyết nhiệm vụ đặt ra.
Các phần tiếp theo sẽ cho bạn thêm hiểu biết về cách đạt được điều đó!
Thụt lề
Khi lập trình bằng Python, thụt lề là thứ bạn chắc chắn sẽ dùng. Tuy nhiên, bạn nên cẩn thận vì nó có thể dẫn tới lỗi cú pháp. Khuyến nghị là dùng bốn khoảng trắng để thụt lề. Ví dụ, câu lệnh này dùng bốn khoảng trắng để thụt lề:
Và vòng lặp
forvới câu lệnhprintnày cũng thụt lề bằng bốn khoảng trắng:Khi bạn viết một biểu thức dài, tốt nhất nên căn chỉnh dọc biểu thức. Làm vậy bạn sẽ tạo ra "hanging indent".
Dưới đây là một số ví dụ về hanging indent trong các biểu thức dài, cho thấy vài biến thể cách bạn có thể dùng:
Mỗi lập trình viên làm việc với Python hay ngôn ngữ khác đều tự hỏi liệu nên dùng tab hay khoảng trắng để thụt lề. Sự khác nhau giữa tab và khoảng trắng là cuộc tranh luận kéo dài trong cộng đồng. Hãy xem, chẳng hạn, bài viết trên Stackoverflow này.
Nhìn chung, khoảng trắng là cách thụt lề được ưa chuộng, nhưng nếu bạn thấy một số script Python đã dùng tab, bạn nên tiếp tục thụt lề bằng tab. Nếu không, bạn nên đổi thụt lề của mọi biểu thức trong script sang dùng khoảng trắng.
Lưu ý rằng Python 3 không cho phép trộn tab và khoảng trắng để thụt lề. Vì vậy bạn nên chọn một trong hai và kiên định với nó!
Độ dài dòng tối đa
Nhìn chung, nên đặt mục tiêu độ dài dòng là 79 ký tự trong mã Python của bạn.
Tuân theo con số mục tiêu này có nhiều lợi ích. Một vài trong số đó là:
Comment nên có độ dài dòng 72 ký tự. Bạn sẽ học thêm về các quy ước phổ biến cho comment ở phần sau của hướng dẫn này!
Cuối cùng, nếu bạn làm việc trong nhóm nhỏ, việc chọn theo quy ước và phong cách nào là tùy bạn, và đa số lập trình viên chấp nhận việc đôi khi lệch khỏi hướng dẫn độ dài dòng tối đa. Tuy nhiên, nếu bạn tạo hoặc đóng góp cho dự án mã nguồn mở, bạn có lẽ sẽ muốn và/hoặc cần tuân thủ quy tắc độ dài dòng tối đa được PEP-8 đặt ra.
Khi dùng toán tử
+, tốt hơn là dùng xuống dòng hợp lý, giúp mã dễ hiểu hơn:total = (A +B +
C)
total = (A+ B
+ C)
Ngoài ra, bạn cũng có thể viết:
Tóm lại, bạn có thể xuống dòng trước hoặc sau toán tử nhị phân, miễn là nhất quán. Nếu bạn đang viết mã mới, hãy cố gắng theo lựa chọn cuối cùng đã trình bày, nơi bạn xuống dòng trước toán tử nhị phân.
Dòng trống
Trong các script Python, các hàm và lớp cấp cao (top-level) được ngăn cách bởi hai dòng trống. Định nghĩa phương thức bên trong lớp nên ngăn cách nhau bởi một dòng trống. Bạn có thể thấy rõ trong ví dụ sau:
Các lớp
SwapTestSuitevàOddOrEvenTestSuiteđược ngăn cách bởi hai dòng trống, trong khi các định nghĩa phương thức như.setUp()và.test_swap_operations()chỉ có một dòng trống để ngăn cách. Mã sẽ chạy thành công, nhưng sẽ không tạo ra đầu ra nào vì thiếu mã cần thiết để chạy unit test (chỉ minh họa thực hành tốt).Khoảng trắng trong biểu thức và câu lệnh
Bạn nên tránh khoảng trắng khi thấy mã của mình viết như trong các ví dụ sau:
func(data, {pivot: 4})func( data, { pivot: 4 } )indexes = (0,)indexes = (0, )if x == 4: print x, y; x, y = y, xif x == 4 : print x , y ; x , y = y , xspam(1)spam (1)dct['key'] = lst[index]dct ['key'] = lst [index]x = 1y = 2
long_variable = 3
x = 1y = 2
long_variable = 3
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
ham[lower + offset:upper + offset]ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
i = i + 1submitted += 1
x = x2 - 1
hypot2 = xx + yy
c = (a+b) (a-b)
i=i+1submitted +=1
x = x 2 - 1
hypot2 = x x + y y
c = (a + b) (a - b)
def complex(real, imag=0.0):return magic(r=real, i=imag)
def complex(real, imag = 0.0):return magic(r = real, i = imag)
def munge(input: AnyStr): ...def munge() -> AnyStr: ...
def munge(input:AnyStr): ...def munge()->PosInt: ...
def munge(sep: AnyStr = None): ...def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
def munge(input: AnyStr=None): ...def munge(input: AnyStr, limit = 1000): ...
if foo == 'blah':do_blah_thing()
do_one()
do_two()
do_three()
if foo == 'blah': do_blah_thing()do_one(); do_two(); do_three()
hoặc
if foo == 'blah': do_blah_thing()for x in lst: total += x
while t < 10: t = delay()
hoặc
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()
try: something()
finally: cleanup()
do_one(); do_two(); do_three(long, argument,list, like, this)
if foo == 'blah': one(); two(); three()
FILES = ('setup.cfg',)FILES = 'setup.cfg',FILES = ['setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
FILES = ['setup.cfg', 'tox.ini',]initialize(FILES, error=True,)
Các ví dụ này được lấy từ PEP-8.
Mã hóa tệp nguồn
Một máy tính không thể lưu trữ "chữ", "số", "hình ảnh" hay bất kỳ thứ gì khác; Nó chỉ có thể lưu trữ và làm việc với bit, vốn chỉ có giá trị nhị phân:
yeshoặcno,truehoặcfalse,1hoặc0, v.v. Như bạn đã biết, máy tính hoạt động bằng điện; Điều này có nghĩa là một bit "thực" là sự có mặt hay vắng mặt của một xung điện. Thông thường bạn biểu diễn sự (không) có mặt này bằng1và0.Để dùng bit biểu diễn bất cứ thứ gì khác ngoài chính bit, bạn cần một bộ quy tắc. Bạn cần chuyển một chuỗi bit thành thứ như chữ cái, con số và hình ảnh bằng một lược đồ mã hóa hay encoding. Ví dụ về lược đồ mã hóa là ASCII, UTF-8, v.v.:
Mẹo: nếu bạn muốn biết thêm về mã hóa, hãy xem bài viết này.
Vậy điều này quan trọng vì sao?
Bạn sẽ nhận ra rằng chuỗi (string) là một trong những kiểu dữ liệu được dùng nhiều nhất trong Python. Như bạn có thể đoán, sẽ có lúc bạn muốn làm việc với chuỗi có chứa hoặc hoàn toàn gồm các ký tự không thuộc bộ ASCII chuẩn. Suy cho cùng, có thể bạn phải làm việc với văn bản chứa ký tự có dấu, như
á,ž,çv.v.Trong Python 3, UTF-8 là mã hóa nguồn mặc định. Nhưng với những ai dùng Python 2, bạn có lẽ đã biết mặc định khi đó là ASCII.
Vậy nếu bạn có một chuỗi chứa ký tự không phải ASCII, như
"Flügel"thì sao?Khi bạn tham chiếu chuỗi trong Python 2, bạn sẽ nhận được như sau:
Trông không giống chuỗi của bạn! Vậy khi in ra thì sao?
In ra cho bạn giá trị đã gán cho biến. Ký tự không thuộc ASCII
ÃŒđã được mã hóa. Đó là lý do bạn nhận về\xfckhi tham chiếu chuỗi. Để xử lý việc này, bạn có thể dùng các phương thức chuỗi.encode()và.decode(). Cái trước trả về phiên bản chuỗi 8-bit của chuỗi Unicode, được mã hóa theo encoding yêu cầu, trong khi cái sau diễn giải chuỗi theo encoding đã cho.Import
Import thư viện và/hoặc mô-đun là việc bạn sẽ thường làm khi làm việc với Python cho khoa học dữ liệu. Như bạn có thể đã biết, bạn nên luôn import thư viện ở đầu script.
Lưu ý rằng nếu bạn có nhiều import, hãy đảm bảo mỗi import ở trên một dòng riêng.
Xem bảng sau để hiểu rõ hơn:
from config import settingshoặc
import osimport sys
import os, sysThêm nữa, bạn cần lưu ý có một thứ tự cần tuân theo khi import thư viện. Nói chung, bạn có thể theo thứ tự này:
Import tuyệt đối và tương đối
Tiếp theo, tốt để biết sự khác nhau giữa import tuyệt đối và tương đối. Nói chung, import tuyệt đối được ưu tiên trong Python vì tăng tính dễ đọc. Tuy nhiên, khi ứng dụng của bạn trở nên phức tạp hơn, bạn cũng có thể dùng import tương đối. Import tương đối ngầm định không bao giờ nên dùng và đã bị loại bỏ trong Python 3.
Vậy import tuyệt đối và tương đối là gì?
Import tuyệt đối là import sử dụng đường dẫn tuyệt đối của hàm hoặc lớp, phân tách bằng
.. Ví dụ,Import tương đối là import tương ứng với vị trí hiện tại nơi tệp Python của bạn tồn tại. Bạn có thể dùng kiểu import này nếu cấu trúc dự án của bạn lớn dần, vì nó sẽ làm dự án dễ đọc hơn. Nghĩa là, nếu bạn có cấu trúc dự án Python như sau:
Bạn có thể dùng import tương đối để import thuật toán sắp xếp nổi bọt
BubbleSort, được lưu trongbubble_sort.pyvàotest1. Sẽ như sau:Nếu muốn biết thêm về import tuyệt đối và tương đối, bạn có thể xem PEP 328.
Import wildcard
Cuối cùng, bạn nên tránh import wildcard, vì chúng không giúp tăng khả năng đọc; Bạn sẽ không biết rõ đang dùng những lớp, phương thức hay biến nào từ mô-đun của mình, ví dụ:
Comment
Comment được dùng cho tài liệu nội tuyến trong Python. Chúng giúp hiểu mã tốt hơn. Có nhiều công cụ bạn có thể dùng để tạo tài liệu, như comments và docstrings, cho mô-đun của chính bạn. Comment nên chi tiết hơn để khi ai đó đọc mã, họ có thể hiểu đúng về mã và cách nó được dùng với các phần khác của mã.
Comment bắt đầu với ký hiệu
#. Bất cứ điều gì viết sau dấu thăng sẽ không được thông dịch viên thực thi. Ví dụ, đoạn mã sau sẽ chỉ in ra"This is a Python comment".Hãy nhớ: ở phần trước, bạn đã đọc rằng comment nên có độ dài dòng 72 ký tự!
Như vậy, có ba loại comment:
#và một khoảng trắng. Nếu cần nhiều hơn một đoạn, chúng nên được tách bằng một dòng chỉ chứa một#.Xem trích đoạn sau, lấy từ thư viện
scikit-learn, để hiểu các comment này trông thế nào:#và một khoảng trắng.Ví dụ:
"""và kết thúc với""":Tên dunder cấp mô-đun
Giờ bạn đã đọc docstring là gì, bạn cũng nên biết về các dunder cấp mô-đun, tức các tên có hai gạch dưới ở đầu và cuối, rất hữu hiệu trong Python. Đây là các tên đặc biệt mà Python định nghĩa để không xung đột với các hàm hay tên do người dùng định nghĩa. Để biết thêm, bạn có thể xem bài viết này.
Một dunder cấp mô-đun như (
__all__,__author__,__version__) nên được đặt sau docstring chính của mô-đun và trước tất cả câu lệnhimport. Bạn nên định nghĩa các importfrom __future__trước bất kỳ mã nào khác, ngoại trừ docstring:Mẹo: xem các quy ước để viết docstring.
Quy ước đặt tên
Khi lập trình bằng Python, bạn gần như chắc chắn sẽ sử dụng một quy ước đặt tên, tức tập hợp quy tắc để chọn chuỗi ký tự dùng cho định danh biểu thị biến, kiểu, hàm, và các thực thể khác trong mã nguồn và tài liệu.
Nếu bạn chưa rõ có những phong cách đặt tên nào, hãy cân nhắc các kiểu sau:
bhoặc một chữ thường đơn;Bhoặc một chữ hoa đơn;lowercaseUPPERCASElower_case_with_underscoresUPPER_CASE_WITH_UNDERSCORESCapitalizedWords, còn được biết đến làCapWords,CamelCasehoặcStudlyCaps.mixedCaseCapitalized_Words_With_Underscores_single_leading_underscore: chỉ báo yếu cho "dùng nội bộ". ví dụ,from M import *không import các đối tượng có tên bắt đầu bằng gạch dưới.single_trailing_underscore_: dùng theo quy ước để tránh xung đột với từ khóa Python, ví dụ,Tkinter.Toplevel(master, class_='ClassName')__double_leading_underscore: khi đặt tên thuộc tính lớp, sẽ kích hoạt name mangling (bên trong lớpFooBar,__bootrở thành_FooBar__boo).__double_leading_and_trailing_underscore__: các đối tượng hoặc thuộc tính "ma thuật" tồn tại trong không gian tên do người dùng kiểm soát. Ví dụ,__init__,__import__hoặc__file__. Bạn không nên tự nghĩ ra các tên như vậy, chỉ dùng theo tài liệu.Quy ước đặt tên chung
Bảng sau cho bạn một số hướng dẫn chung về cách đặt tên định danh:
0) và (1) trong một số phông chữ.Nếu bạn muốn biết thêm về các ngoại lệ cho quy ước đặt tên chung, hãy xem bài viết này.
Mã của bạn có tuân thủ PEP-8 không?
Sau khi tìm hiểu thêm về PEP-8, có lẽ bạn đang tự hỏi làm sao kiểm tra mã của mình có thực sự tuân thủ các hướng dẫn này (và nhiều nội dung chưa được đề cập trong hướng dẫn này!) hay không.
Bên cạnh việc tự đọc PEP-8 và học thêm về nó, bạn chắc chắn nên cân nhắc tìm hiểu mô-đun tiện dụng
pep8, góicoalavà một số lựa chọn thay thế khác được mô tả trong các phần tiếp theo!Gói Python
pep8Gói
pep8có thể được dùng để kiểm tra sự không tương thích với PEP-8 trong mã Python của bạn và đề xuất thay đổi để tuân theo các hướng dẫn PEP-8. Bạn có thể cài mô-đunpep8bằng pip với lệnh sau:Để minh họa cách
pep8hoạt động, hãy tạo một tệp Python mới tênexample.pyvới đoạn mã sau:Lưu tệp rồi chạy lệnh
pep8trên tệp đó trong terminal hoặc command prompt như sau:Lệnh này sẽ xuất ra mọi điểm không tương thích PEP-8 mà pep8 phát hiện trong mã, như sau:
Dòng đầu cho biết cần có hai dòng trống trước định nghĩa hàm ở dòng 1. Hai dòng tiếp cho biết cần có khoảng trắng quanh toán tử cộng trong câu lệnh
printvà các câu lệnh gán.Để sửa các vấn đề này, bạn có thể chỉnh mã như sau:
Giờ, khi bạn chạy lại
pep8 example.py, sẽ không có đầu ra nào, nghĩa là mã đã tuân thủ PEP-8.Bạn cũng có thể xem mã nguồn nơi phát hiện không tương thích với tham số
--show-source:Hoặc bạn có thể hiển thị tần suất từng lỗi được phát hiện bằng cách thêm
--statistics:Mẹo: cũng hãy xem các mô-đun khác như
flake8,autopep8hoặcpylint!Phân tích mã của bạn với
coalacoalacung cấp linting và sửa lỗi cho mọi ngôn ngữ, nhưng ở đây bạn quan tâm đến Python, bạn có thể càicoalabằngpip:Trong đoạn mã trên, bạn thấy thực ra bạn cài
coala-bears:bearslà các plugin hoặc mô-đun đơn giản mở rộng khả năng củacoalavà khác nhau theo ngôn ngữ. Trong trường hợp này, bạn muốn dùngpep8bear, tìm mã không tương thích PEP-8 và sửa trực tiếp. Bạn chắc chắn nên cân nhắc dùng công cụ này để kiểm tra mã Python của bạn.pep8online: Kiểm tra mã Python của bạn trực tuyến
Bên cạnh mô-đun
pep8tiện dụng và góicoala, bạn cũng có thể kiểm tra mã Python có tuân thủ PEP-8 hay không bằng cách truy cập pep8online. Trang này có trình soạn thảo trực tuyến cho phép bạn dán mã vào, nhấn nút "Check code"! Kết quả, bạn sẽ nhận được phản hồi về những gì cần cải thiện. Tiện lợi!Kết luận
Khi dùng Python, đôi khi bạn không chú ý đến chất lượng mã vì áp lực phát hành tính năng nhanh. Tuy nhiên, các thực hành được mô tả trong hướng dẫn này -và nhiều điều nữa chưa được đề cập- nên là một phần của chu trình phát triển-dàn dựng-kiểm thử-triển khai của bạn. Điều này có lợi cho mọi người trong dự án để hiểu và phần lớn thời gian có thể thay đổi mã mà không cần đào sâu và hiểu mã bằng cách khởi động trình gỡ lỗi. Nếu bạn làm việc trên dự án mã nguồn mở, những người đóng góp sẽ thấy PEP-8 thật tuyệt và hiểu mã của bạn tốt hơn vì đây là tiêu chuẩn phổ quát, mỗi lập trình viên Python đều theo.
Giờ bạn đã đi qua hướng dẫn này, một ý tưởng rất hay là tự mình xem PEP-8! Còn rất nhiều điều để khám phá.
Nếu bạn có thêm mẹo nào để tuân thủ PEP-8 hoặc nghĩ rằng chúng tôi đã bỏ sót điều gì quan trọng trong bài viết, đừng ngần ngại cho chúng tôi biết tại @DataCamp.