Tin tức và sự kiện

Thông báo 14/11/2018

[CODEWELL] PYTHON CODE STYLE - PART 2

Python đơn giản, mạnh và linh hoạt; là sự lựa chọn tốt cho tất cả những người mới bắt đầu cũng như những người đã thành thạo. Đã có rất nhiều “dẫn chứng” tuyệt vời về Python cũng như tương lai sáng lạn của ngôn ngữ lập trình này qua các bảng xếp hạng mức độ phổ biển của các loại ngôn ngữ. Tiếp nối phần 1, CO-WELL Asia tiếp tục chia sẻ cùng các bạn phần 2 của loạt bài Python Code Style.

I. Idioms

Idioms trong programing đơn giản là cách để viết dòng code cho đúng, cho chuẩn với style của ngôn ngữ lập trình.

Đối với Python thì Idioms đơn giản là hãy viết cho thật "Pythonic" - đơn giản nhất, dễ đọc nhất, sử dụng cách của Python chứ không phải của ngôn ngữ khác.

Dưới đây là một số Idoms phổ biến của Python nhé:

1. Unpacking

Unpacking được đề cập khi bạn làm việc với các phần tử của mảng (inerrable: List, Tuple, Dictionary...)

Nếu chúng ta biết được chính xác độ dài của từng phần tử trong mảng, chúng ta nên đặt tên cho các phần tử con khi bóc tách.

Ví dụ:

Ta có 1 list tọa độ các địa điểm, và cần bóc tách để in ra output chẳng hạn:

places = [(21, 105), (15, 40), (23, 52)]

for lat, long in places:

print ("Latitude: {} | Longitude: {}".format(lat, long))

Python có thể sử dụng kiểu bóc tách này để hoán đổi giá trị của 2 biến.

Ví dụ:

a = 10

b = 5

a, b = b, a

Cũng ok với bóc tách lồng nhau nữa:

a, (b, c) = 1, (2, 3)

Đối với Python3 thì việc bóc tách này trở nên linh hoạt hơn:

            a, *rest = [1, 2, 3]

            # a = 1, rest = [2, 3]

            a, *middle, c = [1, 2, 3, 4]

            # a = 1, middle = [2, 3], c = 4

2. Create an ignored variable - Tạo biến và không dùng đến nó

Khi chúng ta cần tạo ra biến mà ko dùng đến nó (khi để unpacking chẳng hạn), hãy sử dụng "__"

Ví dụ:

            students = [('John', '17', 'male'), ('Ana', '37', 'female')]

            for name, age, __ in students:

            print ("Name: {}, Age: {}".format(name, age))

3. Tạo ra mảng với N phần tử giống nhau

Hãy sử dụng phép nhân (*)                                   

            dot = 'this is red dot'

            four_dots = [dot] * 4

            print(four_dots)

            # ['this is red dot', 'this is red dot', 'this is red dot', 'this is red dot']

4. List comprehension

List comprehension cực kỳ phổ biến, và tạo ra sự khác biệt cho Python. Nó là cách viết thu gọn khi tạo ra 1 list.

            students = [('John', '17', 'male'), ('Ana', '37', 'female'), ('Min', '1', 'unknow')]

            student_names = [name for name, __, __ in students]

            print(student_names)

            # ['John', 'Ana', 'Min']       

Nó tương đương với:

            students = [('John', '17', 'male'), ('Ana', '37', 'female'), ('Min', '1', 'unknow')]

            student_names = []

            for name, __, __ in students:

            student_names.append(name)

            print(student_names)

            # ['John', 'Ana', 'Min']                     

Thay vì dùng 3 dòng để duyệt qua và thêm phần tử vào list, chúng ta chỉ cần 1 dòng với list comprehension

5. Tạo ra chuỗi ký tự từ list

Dùng str.join()

            letters = ['s', 'p', 'a', 'm']

            

            empty_string = ''

            word = empty_string.join(letters)

            print(word)               # spam

            

            space = ' '

            word_with_space = space.join(letters)

            print(word_with_space)    # s p a m

                       

            Cách này có thể dùng với List và Tuple

6. Tìm phần tử trong nhóm

Trong Python, một nhóm có thể là List, Set hay Dictionary...

Cùng xem qua ví dụ dưới đây:

            s = set(['s', 'p', 'a', 'm'])

            l = ['s', 'p', 'a', 'm']

            def lookup_set(s):

                        return 's' in s

            def lookup_list(l):

                        return 's' in l                      

Về mặt ý nghĩa thì 2 function này hoàn toàn giống nhau, mục đích là tìm phần tử trong nhóm.

Nhưng có một điểm rất khác biệt ở đây mà chúng ta cần chú ý, đó là Hiệu Năng!

Đối với List, để biết được một phần tử X có trong list đó không, Python phải duyệt qua từng phần tử trong list cho đến khi tìm thấy phần tử khớp với phần tử X cần tìm.                       

Theo cách này thì sẽ tốn kha khá thời gian, đặc biệt là đối với những list dài.

Set thì hoạt động theo cách khác. Do Set có thuộc tính "hashable" nên Python biết chính xác phần tử cần tìm nằm ở đâu trong Set.

Dó đó việc tìm kiếm sẽ thu được kết quả nhanh chóng, kể cả khi đó là 1 Set cực lớn.

Các bạn có thể tìm hiểu thêm về khái niệm hashable ở đây: https://www.quora.com/What-is-meant-by-hashable-in-the-context-of-Python-sets

Việc tìm kiếm trong Dictionary cũng hoạt động tương tự như đối với Set.                       

Chính vì sự khác biệt lớn về hiệu năng nên sẽ tốt hơn rất nhiều nếu chúng ta sử dụng Set hoặc Dictionary thay vì List trong các trường hợp thỏa mãn:

            - Nhóm có một số lượng lớn phần tử

            - Phải thực hiện việc tìm kiếm nhiều lần trên cùng 1 nhóm

            - Trong nhóm không có phần tử trùng nhau           

Đối với các nhóm nhỏ ít phần tử, nếu dùng Set thì thời gian và bộ nhớ cần thiết để khởi tạo bảng hash thường sẽ lớn hơn thời gian tiết kiệm được do hiệu năng tốt mang lại.

Do đó trường hợp này dùng List sẽ tốt hơn.

II. Zen of Python

Đây là những nguyên tắc trong thiết kế của Python, còn được biết đến với tên gọi PEP 20.

Chúng ta có thể xem được những nguyên tắc này bằng cách bật console của Python và nhập lệnh sau:

>>> 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!

III. PEP 8

PEP 8 là một bộ quy chuẩn về cách code Python. Nó là bắt buộc ở hầu hết các dự án OpenSource trên thế giới và đặc biệt hữu dụng khi làm việc nhóm, việc code theo 1 chuẩn chung là rất cần thiết.

Chúng ta có thể cài đặt công cụ để kiểm tra Code mình viết đã đúng chuẩn PEP 8 hay chưa

                        $ pip install pycodestyle

Và một công cụ khác tự động format lại code theo chuẩn của PEP 8:

                        $ pip install autopep8

IV. Conventions

Dưới đây là một số quy ước phổ biến trong Python:

1. Kiểm tra nếu 1 biến bằng một giá trị:

Bad:

                        if attr == True:

                                    print 'True!'

                        if attr == None:

                                    print 'attr is None!'

Good:

                        # Chỉ cần kiểm tra biến đó

                        if attr:

                                    print 'attr is truthy!'

                        # hoặc kiểm tra ngược lại

                        if not attr:

                                    print 'attr is falsey!'

                        # đối với kiểm tra None thì cần cụ thể, vì có thể lẫn với False

                        if attr is None:

                                    print 'attr is None!'           

2. Truy cập phần tử của Dictionary

Không sử dụng phương thức dict.has_key(), thay vào đó sử dụng cú pháp "x in d", hoặc dùng giá trị mặc định trong phương thức dict.get().

Bad:

                        d = {'hello': 'world'}                                  

                                    if d.has_key('hello'):

                                                print d['hello']    # prints 'world'

                                    else:

                                                print 'default_value'                       

                        Good:

                                    d = {'hello': 'world'}

                                    print d.get('hello', 'default_value')  #   'world'

                                    print d.get('thingy', 'default_value') #   'default_value'

                                    # Hoặc:

                                    if 'hello' in d:

                                                print d['hello']           

3. Sử dụng những cách ngắn gọn để tạo ra List

Như đã đề cập đến ở trên, Python có List comprehension cho phép tạo ra List chỉ với một dòng code

Ngoài ra Python còn có map() và filter(), cho phép tạo ra list với cú pháp ngắn gọn hơn.

4. Lọc phần tử trong List

Bad:

                        # Lọc phần tử lớn hơn 4

                        a = [3, 4, 5]

                        for i in a:

                        if i > 4:

                        a.remove(i)

Đoạn code trên chắc chắn sẽ trả vể kết quả sai do thay đổi độ dài, thứ tự của List đang truy cập.

=> Không được phép xóa phần tử trong List trong khi đang lặp qua nó.

Good:

                        # Lọc phần tử lớn hơn 4

                        a = [3, 4, 5]             

                        # Sử dụng list comprehension

                        greater_than_4 = [number for number in a if number > 4]

                        # Sử dụng hàm filter()

                        greater_than_4 = list(filter(lambda i: i > 4, a))

                                   

5. Sửa đổi phần tử của List

Có một điều cần đặc biệt lưu ý:

Khi tạo một biến mới và gán nó bằng một list có sẵn, Python sẽ không tạo ra một list mới, mà chỉ ánh xạ đến cùng 1 list mà thôi.

Ví dụ:

                        # Cộng mỗi phần tử trong list thêm 3

                        a = [3, 4, 5]

                        b = a                     # a và b cùng trỏ đến list [3, 4, 5]

                        for i in a:

                        i += 3             # b cũng sẽ thay đổi                       

Vậy làm sao để thực hiện điều đó cho đúng?

Hãy tạo ra một list mới hoàn toàn:

                        b = a[::]

                        b = list(a)                                   

                        # Python 2:

                        import copy

                        b = copy.copy(a)                                   

                        # Python 3:

                        b = a.copy()

6. Đọc file

Sử dụng cú pháp "with open"

Bad:

                        f = open('file.txt')

                        a = f.read()

                        print a

                        f.close()                       

Good:

                        with open('file.txt') as f:

                        for line in f:

                        print line                       

Dùng "with" tốt hơn vì nó luôn tự động đóng file sau khi thực hiện đọc-ghi                       

7. Một câu lệnh trên nhiều dòng

Điều này xảy ra khi một câu lệnh dài hơn độ dài giới hạn của một dòng code trong Python (theo chuẩn PEP 8 là 79 ký tự)

Có 2 cách để làm điều này, dùng backslash (\) hoặc dấu ngoặc đơn ()

Sử dụng dấu backslash có thể nhanh hơn trong 1 số trường hợp, nhưng sẽ gây ra vấn đề trả về kết quả không mong đợi khi có ký tự trắng sau dấu backslash.

Vì vậy cần tránh sử dụng dấu này.

Bad:

                        my_very_big_string = """For a long time I used to go to bed early. Sometimes, \

                        when I had put out my candle, my eyes would close so quickly that I had not even \

                        time to say “I’m going to sleep.”"""

                        from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \

                        yet_another_nice_function                                                           

Good:

                        my_very_big_string = (

                                    "For a long time I used to go to bed early. Sometimes, "

                                    "when I had put out my candle, my eyes would close so quickly "

                                                "that I had not even time to say “I’m going to sleep.”"

                                    )

                        from some.deep.module.inside.a.module import (

                                    a_nice_function, another_nice_function, yet_another_nice_function)

Tuy nhiên, cả 2 trường hợp này đều nên hạn chế! Vì khi bạn sử dụng nó có nghĩa là bạn đang cố gắng làm quá nhiều thứ trên 1 dòng code, điều đó đi ngược lại với quy tắc code dễ đọc của Python!

 

Tham khảo thêm: Python Code Style Part 1 tại http://www.co-well.vn/news/detail/codewell-python-code-style-part-1

cùng nhiều thông tin hữu ích khác tại fanpage của CO-WELL Asia.

 

Back to top