PEP8 - Đặt Tên Đúng Chuẩn Trong Python Quan Trọng Thế Nào?

PEP8 - Đặt Tên Đúng Chuẩn Trong Python Quan Trọng Thế Nào?

Ở bài viết trước, PEP8 - Chuẩn Kết Nối Toàn Cầu Của Python Dev (Phần 1) chúng ta đã cùng nhau tìm hiểu về tầm quan trọng của chuẩn PEP-8 và  một số chỉ dẫn liên quan đến "Trình bày các đoạn mã nguồn" (có cả lý thuyết và thực hành). Bài viết tiếp theo này, chúng ta sẽ cùng nhau tìm hiểu về các chỉ dẫn liên quan đến đặt tên (naming) được nêu trong PEP-8.

Bài viết này sẽ nhấn mạnh vào hai vấn đến:
- Quy tắc đặt tên theo chuẩn PEP8 của Python
- Khi nào dùng _, __ (underscore & double underscore)

Nói về đặt tên trong các chương trình phần mềm, điều đầu tiên cần phải nhấn mạnh (với tất cả các ngôn ngữ lập trình, không riêng gì python) đó là đặt tên có ý nghĩa, liên quan đến mục đích xuất hiện của biến dữ liệu, function, class,... Tránh việc đặt tên vô nghĩa để người khác đọc code và lại phải mất thời gian tìm hiểu mục đích sử dụng trong ngữ cảnh hiện tại (trừ trường hợp cố tình thực hiện làm "rối" code). Các bạn có thể tìm đọc "Chapter 2 - Meaningful Names - cuốn sách Clean Code của Uncle Bod".

Naming - Các quy ước chung cho việc đặt tên trong Python.

PEP-8 gợi ý các kiểu đặt tên các biến (python được gọi là name), function, class, ... khá là độc đáo và khác với các ngôn ngữ khác. Các quy ước (conventions) này giúp dễ dàng phân biệt từng loại tương ứng khi đọc code. Quy ước chung của cách đặt tên gồm có:

  • Viết thường tất cả các ký tự (lower case)
  • Viết hoa tất cả các ký tự (upper case)
  • Nếu một tên có nhiều từ:
    • Nối giữa các từ bằng ký tự underscore _: lower_case_with_underscores, UPPER_CASE_WITH_UNDERSCORES
    • Viết hoa các chữ cái đầu tiên của một từ: CapWords, CamelCase...
    • Nếu các từ có ký tự viết tắt, lưu ý viết hoa toàn bộ ký tự viết tắt. Ví dụ: HTTPServerError

Ngoài các cách trên, còn một số cách đặt tên khác nhưng ít được dùng (không được khuyên dùng):

  • Viết thường từ đầu tiên và viết hoa các chữ cái đầu của các từ tiếp theo: mixedCase, theUglyName,...
  • Viết hoa các chữ cái đầu tiên và dùng ký tự underscore để nối các từ: Capitalized_Words_With_Underscores, No_Name,...

Trong quá trình làm việc, để gom nhóm theo mục đích sử dụng, các lập trình có thể tiền tố và phía trước biến, function, ... nhưng vẫn theo quy tắc đặt tên chung trên.

Ví dụ: gom các attributes thuộc dữ liệu trả về trong os.start(): st_mode, st_ino, st_dev, ... (tham khảo: https://docs.python.org/2/library/os.html#os.stat)

Có một số lưu ý cần nhớ trong quy tắc đặt tên, tôi đã nêu trong bài viết gần đây: 3 Lỗi Thường Gặp Khi Mới Làm Quen Với Python. Các bạn có thể tham khảo tại mục "1. Đặt trùng tên biến và function với keyword"

Ngoài ra ra còn có một số lưu ý nhỏ khác như:
- Không nên đặt tên bằng 01 ký tự đơn như: i, j, k, l, m, n....
- Trong trường hợp lowser case, nên để ý việc dùng l (letter l-L) và số 1, cũng như ký tự o (letter o-O) và số 0. Việc này sẽ hạn chế được việc hiển thị không rõ ràng các ký tự (do font chữ).

Từ các quy ước chung trên, PEP-8 đưa ra các đề xuất liên quan đến đặt tên cụ thể cho các trường hợp.

1. Đặt tên cho các module/package

Trong python, mỗi file được coi là một module, mỗi folder là một package. Quy tắc đặt tên của module và package là viết thường tất cả các ký tự, nếu có nhiều từ trong tên, dùng ký tự _ để nối giữa các từ.


Ví dụ:

├── random_package
    ├── __init__.py
    ├── random_number.py
    ├── random_string.py

Một số module được kế thừa từ C/C++, có thể đặt tên với ký tự _ ở phía trước.
Ví dụ: _socket

Lưu ý: Khi đặt tên module, package hãy cố gắng đừng đặt trùng tên với các module/package đã có sẵn của hệ thống. Như ở ví dụ trên, tôi đã đặt package là random_package để phân biệt được với module random đã có sẵn.

Nếu dev đặt tên module/package là random, khi thực hiện import random trình thông dịch của Python sẽ khó phân biệt cần import module random có của python hay import module random do dev viết ra (thường thì sẽ import được theo  ngữ cảnh/vị trí sắp xếp structure) và đôi khi kết quả của phần mềm không được như ý muốn.

2. Đặt tên cho các biến dữ liệu (variable/name), function:

Tên của các functions, variables được đặt tên ở định dạng chữ viết thường, nếu có nhiều từ, sẽ nối với nhau bởi ký tự underscore:

Ví dụ:

number = 10
lower_case_with_underscore = "this is a long name sample"

Biến dữ liệu được đặt là constants value (các dữ liệu không thay đổi), thường được viết hoa toàn bộ các ký tự và có dấu _ nối giữa các từ

ví dụ:
SECONDS_IN_HOUR = 3600
SECONDS_IN_DAY = 86400

Đôi lúc trong quá trình khai báo và sử dụng biến, có những biến dữ liệu không dùng để làm gì sẽ được khai báo là dấu _ (ý nghĩa là "don’t care")

ví dụ:

members = ('Trang', 'An', 'Mai')
frist_member, _, last_member = members
print(first_member, last_member)

3. Đặt tên cho class và attribute/function trong class:

Các class trong Python được đặt tên theo định dạng CapitalizedWord (hoặc CamelCase).

Ví dụ: 

class RandomNumber:
    
    def __init__(self):
        pass
Một số class đặc biệt trong Python được kết thúc bằng một từ khóa đặc biệt, ví dụ: Error cho các class thuộc phân lớp Exception: ValueError, NameError,...

Quy ước đặt tên với attribute/function trong class:
  • Mặc định, các attribute/function của một lớp được khai báo là public, quy tắc đặt tên như đặt tên biến (lower_case_with_underscore) 
  • Nếu khai báo protected , đặt 01 ký tự _ ở đầu tiên: _protected_attribute_name
  • Nếu khai báo private, đặt 02 ký tự _ ở đầu tiên: __private_attribute_name  (2 dấu underscore liên tiếp)

Quy ước khai báo tham số truyền vào function trong class:
  • Các function nằm trong một class, luôn phải có từ khóa self là tên của tham số đầu tiên (để tham chiếu về đối tượng)
  • Các function dạng "class method", luôn phải có từ khóa cls là tên của tham số đầu tiên (hiện tại, các dev thường sử dụng decorator hơn là dùng classmethod)

Ví dụ: 

class RandomNumber:
    
    def __init__(self):
        self.seed_number = 0
        self._step = 1
        self.__version = "1.0"
    
    def export_random_numbers(self, max_number=100):
        pass

    @classmethod
    def func(cls, export_random_numbers):
        pass

Tổng kết

Ngoài việc mang ý nghĩa về "meaningful names", việc đặt tên trong Python còn mang ý nghĩa về "ngữ pháp" (syntax) của chương trình thông qua việc sử dụng các ký tự _ và __ trong lập trình hướng đối tượng (Object Oriented Programming - OOP). 

Hy vọng, thông qua bài viết ngắn này, các bạn sẽ các có "từ khóa" để tiếp tục tìm hiểu về việc đặt tên sao cho hiệu quả (clean-code) và tự tin với việc sử dụng OOP trong Python (đón chờ bài viết tiếp theo của tôi về chủ đề OOP trong Python nhé).  Cảm ơn các bạn đã đọc bài viết.