[CODEWELL] PYTHON CODE STYLE – PART 1

12/11/2018 1164
python code style 1
CODEWELL

Theo báo cáo mới nhất cuối tháng 8/2018 của TIOBE Index, Python chính thức trở thành ngôn ngữ lập trình phổ biến thứ 4 trong giới công nghệ, chỉ đứng sau Java, C và C++. Thời đại của Python đang bắt đầu như là một người kế nhiệm của Perl và ngôn ngữ lập trình này đang dần trở nên phổ biến hơn khi được sử dụng trong các hệ thống nhúng lớn.

Nếu bạn hỏi một lập trình viên Python điều mà họ thích nhất ở ngôn ngữ lập trình này, câu trả lời phần lớn sẽ là tính “dễ đọc” của nó. Một trong những lý do khiến Python dễ đọc bởi nó sở hữu các nguyên tắc “code style”và các thành ngữ “Pythonic” khá hoàn chỉnh.

A. Các khái niệm chung

Cùng tìm hiểu các khái niệm cơ bản nhất về Python Code Style nào!

1. Explicit Code

Với Python, lập trình viên có thể sử dụng nhiều cách code khác nhau để giải quyết vấn đề, nhưng cách thức viết code rõ ràng và đơn giản nhất luôn được ưu tiên.

Trong đoạn code đẹp dưới đây, function nhận và chỉ nhận 2 tham số, đồng thời trả về một object rõ ràng (dict với cấu trúc định sẵn). Một lập trình viên khi đọc đoạn code này sẽ biết chính xác anh ta có thể làm gì với nó, trong khi đó sẽ rất mơ hồ với đoạn code xấu (số lượng tham số truyền vào, cấu trúc của dict trả về).

2. Một câu lệnh trên mỗi dòng code

Trong Python, mỗi dòng code chỉ chứa một câu lệnh.

Những câu lệnh phức hợp như list comprehension thường được đánh giá cao về sự đơn giản, ngắn gọn, rất “Pythonic nhưng sẽ rất tệ nếu tận dụng sự đơn giản, ngắn gọn đó bằng cách để hai câu lệnh rời rạc trên cùng một dòng code.

3. Các tham số của hàm

Các tham số có thể truyền cho các hàm theo bốn cách khác nhau.

3.1. Positional arguments

Positional arguments là tham số bắt buộc và không có giá trị mặc định. Khi gọi hàm cần truyền đúng thứ tự tham số.

Ví dụ: Trong send(message, recipient) hay point(x, y), lập trình viên có thể dễ dàng nhớ được hai hàm đó yêu cầu hai tham số và theo thứ tự nào.

Trong hai trường hợp này, có thể sử dụng tên tham số khi gọi các hàm và có thể chuyển đổi thứ tự các tham số. Ta có thể gọi send(recipient=’World’, message=’Hello’) và point(y=2, x=1) nhưng điều này làm giảm khả năng dễ đọc và không cần thiết. Vì vậy, cách gọi đơn giản hơn như send(‘Hello’, ‘World’) and point(1, 2) rõ ràng hiệu quả hơn nhiều.

3.2 Keyword Arguments

Keyword Arguments là những tham số không bắt buộc và có giá trị mặc định, thường được sử dụng khi hàm cần dùng đến tham số tùy chọn.
Khi một hàm có nhiều hơn hai hoặc ba Positional Arguments, tên và vị trí của nó sẽ khó nhớ hơn. Lúc này, việc sử dụng các Keyword Arguments với các giá trị mặc định rất hữu ích.

Ví dụ, một hàm send hoàn chỉnh hơn có thể được định nghĩa là send(message, to, cc = None, bcc = None). Ở đây cc và bcc là tùy chọn và là None khi chúng không được cho giá trị lúc gọi.

Gọi một hàm với các tham số từ khóa có thể được thực hiện theo nhiều cách trong Python, ví dụ có thể theo thứ tự các tham số trong định nghĩa mà không đặt tên rõ ràng các tham số, như send (‘Hello’, ‘World’, ‘Cthulhu’ , ‘Amazing’). Cũng có thể đặt tên các đối số theo thứ tự khác, như send (‘Hello’, ‘World’, bcc = ‘Amazing’, cc = ‘Cthulhu’). Hai cách này nên được tránh vì không có bất kỳ lý do nào để không tuân theo cú pháp gần nhất với định nghĩa hàm: send (‘Hello’, ‘World’, cc = ‘Cthulhu’, bcc = ‘Amazing’).

3.3 Arbitrary Argument Sequence
Đây là cách thứ ba để truyền tham số cho hàm, không giới hạn tham số truyền vào.

Ví dụ, send (message, *args) có thể được gọi với nhiều tham số: send(‘Hello’, ‘World’, ‘Mom’, ‘Cthulhu’), và trong hàm , args sẽ bằng (‘World’, Mom, ‘Cthulhu’).

Tuy nhiên, cấu trúc này có một số nhược điểm và nên được sử dụng thận trọng. Do đặc tính không giới hạn tham số truyền vào nên lập trình viên cần lưu ý các trường hợp có thể xảy ra để hạn chế bug.

Ở đây, nếu send có nhiều người nhận, tốt hơn là định nghĩa nó một cách rõ ràng: send(message, receivers) và gọi nó bằng send(‘Hello’, [‘World’, ‘Mom’, ‘Cthulhu’]). Bằng cách này, người dùng của hàm có thể nhập danh sách người nhận dưới dạng list ([]) và nó có thể truyền bất kỳ list nào.

3.4 Arbitrary Keyword Argument Dictionary
Đây là cách cuối cùng để truyền tham số cho các hàm. Nếu hàm yêu cầu một loạt các tham số được đặt tên không xác định thì có thể sử dụng cấu trúc **kwargs.

Ví dụ:
def send(message, **kwargs)
pass

send(message=’xx’, receipter1=’a’, receipter2=’b’, receipter3=’c’)
= > kwargs = {‘ receipter1’: ‘a’, ‘ receipter2’: ‘b’, ‘ receipter3’: ‘c’}

Khi sử dụng loại hình này cũng cần thận trong như trường hợp danh sách tham số tùy ý vì các lý do tương tự.

4. Simple is the best
Là một ngôn ngữ lập trình mạnh mẽ, Python đi kèm với một tập hợp rất nhiều công cụ cho phép lập trình viên làm các thủ thuật can thiệp sâu vào hệ thống.

Ví dụ, các lập trình viên có thể thực hiện các thao tác sau:
• Thay đổi cách đối tượng được tạo và khởi tạo
• Thay đổi cách imports các modules của trình thông dịch Python
• Nhúng các đoạn code C vào Python

Tuy nhiên, tất cả các tùy chọn này có nhiều nhược điểm. Tốt nhất nên sử dụng cách đơn giản nhất để đạt được mục tiêu của bạn.
Hạn chế lớn nhất khi sử dụng các công cụ và thủ thuật là giảm khả năng dễ đọc. Nhiều công cụ phân tích code, ví dụ như pylint hoặc pyflakes sẽ không thể phân tích mã “ma thuật” này.

5. Chúng tôi là những người dùng có trách nhiệm
Python cho phép lập trình viên sử dụng nhiều thủ thuật, bao gồm cả những thủ thuật có tính rủi ro cao. Một ví dụ điển hình là bất kỳ mã client nào cũng có thể ghi đè các thuộc tính và phương thức của một đối tượng vì không có từ khóa “private” trong Python. Triết lý này, rất khác với các ngôn ngữ có tính phòng thủ cao như Java (cung cấp rất nhiều cơ chế để ngăn chặn bất kỳ sự lạm dụng nào).

Điều này không có nghĩa là không có thuộc tính nào được coi là riêng tư và không có khả năng đóng gói thích hợp trong Python. Thay vào dựa vào các bức tường bê tông được xây dựng bởi các nhà phát triển giữa code của họ và của người khác, cộng đồng Python thích dựa vào một tập hợp các quy ước chỉ ra rằng những yếu tố này không nên được truy cập trực tiếp.

Quy ước để private các chi tiết cài đặt là tiền tố tất cả các “nội bộ” với một dấu gạch dưới_ (weak “internal use” indicator) hoặc 2 dấu gạch dưới cho các phần tử của class (name mangling). Nếu mã client phá vỡ các quy tắc này, nếu cố tình sửa đổi thì client sẽ tự chịu trách nhiệm.

6. Giá trị trả về
Khi một hàm trở nên phức tạp, việc sử dụng nhiều câu lệnh return trong hàm là không hiếm gặp. Tuy nhiên, để rõ ràng và dễ dọc, tốt nhất nên tránh return các giá trị có ý nghĩa nhiều từ điểm output.

Có hai trường hợp chính để return giá trị trong hàm: 1 là hàm chạy bình thường, không có lỗi và trả về kết quả như mong đợi; 2 là trường hợp lỗi do tham số đầu vào sai hoặc bất kỳ lý do nào khác làm hàm không thể hoàn thành tính toán hay nhiệm vụ của hàm.

Nếu bạn không muốn raise exceptions cho trường hợp thứ hai, sau đó return một giá trị, chẳng hạn như None hoặc False, thì tốt hơn là nên return ngay khi ngữ cảnh sai được phát hiện.

Tuy nhiên, khi một hàm có nhiều kiểu output, quá trình debug sẽ trở nên khó khăn, vì vậy, có một kiểu output duy nhất sẽ tốt hơn. Điều này cũng sẽ giúp rút gọn một vài đoạn code. Khi một hàm cho nhiều kiểu ouputs, khả năng cao sẽ phải refactoring.

def complex_function(a, b, c):
if not a:
return None # Raising an exception might be better
if not b:
return None # Raising an exception might be better
# Some complex code trying to compute x from a, b and c
# Resist temptation to return x if succeeded
if not x:
# Some Plan-B computation of x
return x # One single exit point for the returned value x will help
# when maintaining the code.

Hy vọng các chia sẻ trên đây sẽ giúp ích được cho các lập trình viên Python trên con đường chinh phục ngôn ngữ này.

Mời các bạn cùng đón xem tiếp phần II của chuyên mục này tại đây hoặc theo dõi các bài viết hữu ích khác trên fanpage của CO-WELL Asia nhé!

 

Tags: ,