Nhiều ngôn ngữ lập trình có một hàm riêng được thực hiện tự động khi một hệ điều hành bắt đầu chạy một chương trình. Hàm này thường được gọi là main() và phải có một cách quay lại cùng với các tham số riêng dựa vào ngôn ngữ tiêu chuẩn. Mặt khác, trình thông dịch Python chạy các tập lệnh bắt đầu ở đầu mỗi tệp, và không có hàm riêng nào mà Python tự động chạy.

Tuy nhiên, có một điểm bắt đầu được xác định cho việc thực hiện một chương trình là hữu ích về việc hiểu cách một chương trình hoạt động. Các lập trình viên Python đã đưa ra một số quy ước để xác định điểm bắt đầu.

Đến cuối bài viết này bạn sẽ hiểu được:

  • Biến __name__ là gì và cách Python xác định nó
  • Lý do bạn muốn sử dụng hàm main() trong Python
  • Có những quy ước nào để xác định hàm main() trong Python
  • Có những “best-practices” nào cho việc đặt code gì vào trong hàm main() của bạn

 

Một hàm main() Python cơ bản

Trong một số tập lệnh Python, bạn có thể thấy một định nghĩa hàm và một câu lệnh điều kiện giống như ví dụ dưới đây:

 

Python
def main():
    print("Hello World!")

if __name__ == "__main__":
    main()

Trong đoạn code này có một hàm gọi là main() cho phép in cụm từ “Hello World!” khi trình thông dịch Python chạy. Ngoài ra còn có một câu lệnh điều kiện (if) để kiểm tra giá trị của hàm __name__ và so sánh với chuỗi “__main__”. Khi câu lệnh if đánh giá là True, trình thông dịch Python sẽ chạy hàm main(). Bạn có thể đọc thêm về các câu lệnh điều kiện trong mục Các Câu Lệnh Điều Kiện Trong Python.

 

Dạng code này khá phổ biến trong các tệp Python mà bạn muốn được chạy như một lệnh và nhập trong một module khác. Để giúp hiểu thêm và cách dạng code này chạy, trước hết bạn nên hiểu cách trình thông dịch cài đặt biến __name__ dựa vào cách mà code này được thực hiện.

 

Các chế độ thực hiện trong Python

 

Có hai cách cơ bản mà bạn có thể hướng dẫn trình thông dịch Python thực hiện hoặc sử dụng code:

  1. Bạn có thể chạy tệp Python như một script sử dụng dòng lênh.
  2. Bạn có thể import code từ một tệp Python đến một tệp khác hoặc đến trình thông dịch tương tác.

Bạn có thể đọc nhiều hơn về những phương pháp này trong mục Làm Thế Nào Để Chạy Các Lệnh Python Của Bạn. Bất cứ cách bạn sử dụng nào để chạy code, Python đều xác định một biến riêng gọi là __name__ bao gồm một chuỗi có giá trị phụ thuộc vào cách sử dụng code.

 

Chúng ta sẽ sử dụng ví dụ này, lưu dưới dạng execution_methods.py để khám phá cách chuyển vận của code tùy thuộc vào ngữ cảnh:

 

Python

print("This is my file to test Python's execution methods.")
print("The variable __name__ tells me which context this file is running in.")
print("The value of __name__ is:", repr(__name__))

Trong tệp này, có ba lệnh gọi print() được xác định. Hai lệnh đầu tiên in một số cụm từ giới thiệu. Lệnh thứ ba print() đầu tiên sẽ in cụm The value of__name__is, sau đó sẽ in biểu diễn của biến __name__ bằng cách sử dụng tích hợp hàm repr().

 

Trong Python, hàm repr() hiển thị biểu diễn in của một đối tượng. Ví dụ này sử dụng hàm repr() để nhấn mạnh rằng giá trị của hàm __name__ là một chuỗi. Bạn có thể đọc thêm về hàm repr() in mục Tài Liệu Python.

 

Bạn sẽ thấy các từ tệp (file), modulelệnh (script) được sử dụng trong suốt bài viết này. Thực tế, không có nhiều sự khác nhau giữa chúng. Tuy nhiên vẫn có những khác nhau nhỏ trong ý nghĩa nhấn mạnh mục đích của một đoạn code:

  1. Tệp (file): Thông thường, một tệp Python là bất kỳ một tệp nào có chứa code. Hầu hết các tệp Python đều có phần mở rộng .py.
  2. Lệnh (script): Một lệnh Python là một tệp mà bạn dự định chạy từ dòng lệnh để hoàn thành một nhiệm vụ.
  3. Module: Một module Python là một tệp mà bạn dự định import từ trong một module hoặc một lệnh khác, hoặc từ trình thông dịch tương tác. Bạn có thể đọc thêm về module trong mục Tài Liệu Python.

Sự phân biệt này còn được thảo luận trong mục Cách Chạy Các Lệnh Python Của Bạn.

 

Chạy tập lệnh từ dòng lệnh

Trong phương pháp này, bạn muốn chạy lệnh Python từ dòng lệnh.

 

Khi bạn chạy một tập lệnh, bạn sẽ không thể tương tác xác định code mà trình thông dịch Python đang chạy. Những chi tiết về cách bạn có thể chạy Python từ dòng lệnh của mình không quan trọng đối với mục đích của bài viết này, nhưng bạn có thể mở hộp dưới đây để đọc thêm về các khác nhau giữa dòng lệnh trên các hệ điều hành Window, Linux và Mac.

   

Bây giờ bạn nên chạy tập lệnh execution_methods.py từ dòng lệnh như dưới đây:

$ python3 execution_methods.py
This is my file to test Python's execution methods.
The variable __name__ tells me which context this file is running in.
The value of __name__ is: '__main__'

Trong ví dụ này, bạn có thể thấy __name__ có giá trị ‘__main__’, trong đó các dấu ngoặc (‘) cho biết giá trị có loại chuỗi.

Hãy nhớ rằng trong Python, không có sự khác nhau giữa các chuỗi được xác định bằng dấu ngoặc đơn (‘) và ngoặc kép (“). Bạn có thể đọc thêm về xác định các chuỗi trong mục Các Loại Dữ Liệu Cơ Bản Trong Python. 

Bạn sẽ tìm thấy kết quả giống nhau nếu bạn có dòng shebang trong tệp lệnh của mình và chạy nó trực tiếp (./execution_methods.py), hoặc sử dụng %run ma thuật trong IPython hoặc Jupyter NoteBooks.

Bạn còn có thể thấy các tập lệnh Python được chạy từ trong các package bằng cách thêm tham số -m vào lệnh. Thông thường bạn sẽ thấy đề xuất này khi bạn sử dụng pip:python3 -m pip install package_name.
Việc thêm tham số -m chạy code trong module __main__.py của một package.
Trong tất cả ba trường hợp trên, __name__ có cùng giá trị: chuỗi ’__main__’.

 

Chi tiết kỹ thuật: 

Tài liệu Python đặc biệt xác định khi nào __name__ sẽ có giá trị ‘__main__’:Một __name__ của module có giá trị ‘__main__’ khi đọc từ input tiêu chuẩn, một tệp lệnh, hoặc từ một dấu nhắc tương tác.__name__ được lưu trữ trong không gian tên toàn cầu của module cùng với __doc__,__package__ và các thuộc tính khác.

Import từ một module hoặc trình thông dịch tương tác

Bây giờ hãy xét đến cách thứ hai mà trình thông dịch Python sẽ chạy code: import. Khi bạn đang phát triển một module hoặc tập lệnh, bạn thường muốn tận dụng các module mà người khác đã xây dựng, đây chính là điều mà bạn làm với từ khóa import.Trong suốt quá trình import, Python chạy các câu lệnh được xác định trong module được chỉ định (nhưng chỉ lần đầu bạn import một module). Để chứng minh các kết quả của việc import tệp execution_methods.py của mình, hãy mở trình thông dịch Python tương tác sau đó import tệp execution_methods.py của bạn:

 

Python

>>> import execution_methods
This is my file to test Python's execution methods.
The variable __name__ tells me which context this file is running in.
The value of __name__ is: 'execution_methods'

Trong kết quả code này, bạn có thể thấy trình thông dịch Python chạy ba lệnh để in print(). Hai dòng đầu tiên của kết quả giống hệt với khi bạn chạy tệp dưới dạng một tập lệnh trên dòng lệnh vì không có biến số trên một trong hai dòng đầu tiên. Tuy nhiên, có một sự khác biệt trên kết quả từ lệnh print() thứ ba.

Khi trình thông dịch Python import code, giá trị của __name__ được đặt giống như tên của module được import. Bạn có thể thấy điều này trong dòng kết quả thứ ba bên trên. __name__ có giá trị ‘execution_methods’. chính là tên của tệp .py mà Python đang import.

Lưu ý rằng nếu bạn import lại module mà không thoát Python thì sẽ không có kết quả.

 

Các “best practice” dành cho các hàm Python chính.

 

Bây giờ bạn có thể nhận thấy những khác biệt trong việc Python xử lý các chế độ thực hiện khác nhau của nó, điều này rất hữu ích để biết một số “best practice” để sử dụng. Chúng sẽ được áp dụng bất cứ khi nào bạn muốn viết code mà bạn có thể chạy dưới dạng một tập lệnh và import trong một module khác hoặc một phiên tương tác.

 

Bạn sẽ học về 4 best practice để đảm bảo code của bạn có thể phục vụ được một mục đích kép:

  1. Đặt hầu hết code vào trong một hàm hoặc class
  2. Sử dụng __name__ để điều khiển quá trình chạy code của bạn
  3. Tạo một hàm gọi là main() để chứa code mà bạn muốn chạy.
  4. Gọi các hàm khác từ hàm main().
 

Đặt hầu hết code vào trong một hàm hoặc class

 

Hãy nhớ rằng trình thông dịch Python chạy tất cả code trong không module khi nó import module. Thỉnh thoảng code bạn viết sẽ có hiệu ứng lề mà bạn muốn người dùng kiểm soát chẳng hạn như:

  • Chạy một phép tính mất nhiều thời gian
  • Ghi vào một tệp trên đĩa
  • In thông tin sẽ làm lộn xộn thiết bị đầu cuối của người dùng.

Trong các trường hợp này, bạn muốn người dùng kiểm soát việc kích hoạt quá trình chạy code này thay vì để trình thông dịch Python chạy code khi nó import module của bạn.

 

Do đó, best practice là bao gồm hầu hết code bên trong một hàm hoặc một class. Vì khi trình thông dịch Python gặp từ khóa def hoặc class, nó chỉ lưu trữ các định nghĩa đó để sử dụng sau này và không thực sự thực hiện chúng cho đến khi bạn nói với nó.

 

Lưu đoạn code bên dưới vào một tệp tên best_practices.py để chứng minh ý này:

 

Python

 

Trong đoạn code này, trước tên bạn import sleep() từ module time.

 

Hàm sleep() tạm dừng trình thông dịch trong nhiều giây như một tham số và sẽ tạo ra một hàm mà mất nhiều thời gian để chạy ví dụ này. Tiếp theo, bạn sử dụng print() để in một câu mô tả mục đích của đoạn code này.

 

Sau đó, bạn xác định một hàm tên là process_data() thực hiện 5 điều sau:

  1. In một số kết quả thông báo với người dùng rằng việc xử lý dữ liệu đang bắt đầu.
  2. Sửa đổi dữ liệu đầu vào
  3. Tạm dừng việc thực hiện ba giây sử dụng hàm sleep()
  4. In một số kết quả thông báo với người dùng rằng việc xử lý đã kết thúc.
  5. Trả lại dữ liệu đã sửa đổi

 

Chạy tệp Best Practice trên dòng lệnh

Bây giờ, điều gì sẽ xảy ra khi bạn chạy tệp này dưới dạng một tập lệnh trên dòng lệnh?

Trình thông dịch Python sẽ chạy các dòng from time import sleep và print() nằm ngoài định nghĩa hàm, sau đó nó sẽ tạo ra định nghĩa của hàm gọi là process_data(). Tiếp đó, tập lệnh sẽ thoát mà không làm gì thêm nữa vì tập lệnh không có một đoạn code nào chạy hàm process_data().

Khối code dưới đây thể hiện kết quả của việc chạy tệp này dưới dạng một tập lệnh:

Shell

$ python3 best_practices.py
This is my file to demonstrate best practices.
Kết quả mà chúng ta thấy ở đây là kết quả của hàm print() đầu tiên. Lưu ý rằng việc import từ time và xác định process_data() không cho ra kết quả. Cụ thể, kết quả đầu ra của các lệnh in print() bên trong định nghĩa của hàm process_data() không được in!

 Import tệp best practice trong một module khác hoặc trình thông dịch tương tác

khi bạn import tệp này trong một phiên tương tác (hoặc một module khác), trình thông dịch Python sẽ thực hiện chính xác các bước như khi nó chạy tệp dưới dạng một tập lệnh.

Một khi trình thông dịch Python import tệp, bạn có thể sử dụng bất cứ biến, class hoặc hàm nào được xác định trong module mà bạn đã import. Để chứng minh điều này, chúng tôi sẽ sử dụng trình thông dịch Python. Bắt đầu trình thông dịch tương tác sau đó nhập import best_practices:

Python

>>> import best_practices
This is my file to demonstrate best practices.
Kết quả duy nhất từ việc nhập tệp best_practices.py là từ lệnh print() đầu tiên được xác định bên ngoài hàm process_data(). Việc import từ time và xác định process_data() không cho kết quả, chỉ giống như khi bạn chạy đoạn code từ dòng lệnh. 
 

Dùng if__name__== "main" để điều khiển việc thực hiện code của bạn

Điều gì sẽ xảy ra nếu bạn muốn hàm process_data() hoạt động khi bạn chạy tập lệnh từ dòng lệnh nhưng không phải khi trình thông dịch Python import tệp? 

Bạn có thể sử dụng thành ngữ if__name__== "main" để xác định ngữ cảnh thực hiện và chỉ chạy một cách có điều kiện hàm process_data() khi __name__ bằng với "__main__". Thêm đoạn code dưới đây vào cuối tệp best_practices.py của bạn:

Python

if __name__ == "__main__":
    data = "My data read from the Web"
    print(data)
    modified_data = process_data(data)
    print(modified_data

Trong đoạn code này, bạn đã thêm một câu lệnh điều kiện kiểm tra giá trị của __name__. Câu lệnh này sẽ đánh giá là True khi __name__ bằng với chuỗi "__main__". Hãy nhớ rằng giá trị riêng của "__main__" cho biến __name__có nghĩa là trình thông dịch Python đang thực hiện tập lệnh của bạn và không import nó. 

Bạn sẽ thêm vào bốn dòng code (các dòng 12, 13, 14 và 15) bên trong khối điều kiện:

  • Các dòng 12 và 13: Bạn đang tạo một biến data dùng lưu trữ dữ liệu mà bạn có được từ Web và in nó.
  • Dòng 14: Bạn đang xử lý dữ liệu.
  • Dòng 15: Bạn đang in dữ liệu đã được sửa đổi.

Bây giờ hãy chạy tập lệnh best_practices.py từ dòng lệnh và xem kết quả sẽ thay đổi như thế nào: 

Shell

$ python3 best_practices.py
This is my file to demonstrate best practices.
My data read from the Web
Beginning data processing...
Data processing finished.
My data read from the Web that has been modified

Đầu tiên, output thể hiện kết quả của lệnh print() bên ngoài hàm process_data().

Sau đó, tập lệnh process_data() và truyền data vào để sửa đổi. Khi process_data() chạy, nó sẽ in một số thông báo trạng thái đến output. Cuối cùng, giá trị của modified_data sẽ được in.

Bây giờ bạn nên kiểm tra xem điều gì xảy ra khi bạn import tệp best_practices.py từ trình thông dịch tương tác (hoặc một module khác). Ví dụ dưới đây sẽ chứng minh:

Python

>>> import best_practices
This is my file to demonstrate best practices.

Lưu ý rằng bạn có cùng một cách chuyển vận như trước khi bạn thêm câu lệnh điều kiện vào cuối tệp! Đây là vì biến __name__ có giá trị "best_practices", nên Python không chạy code bên trong khối, bao gồm cả process_data() vì câu lệnh điều kiện đã đánh giá là False.

Tạo một hàm main() để chứa code mà bạn muốn chạy

Bây giờ bạn có thể viết code Python chạy từ dòng lệnh dưới dạng một tập lệnh và được import mà không có hiệu ứng lề không mong muốn. Tiếp theo, bạn sẽ học cách viết code để giúp các lập trình viên Python khác dễ dàng theo dõi ý của bạn.

Nhiều ngôn ngữ như C, C++, Java cùng với các ngôn ngữ khác định nghĩa một hàm riêng phải được gọi là main() mà hệ điều hành tự động yêu cầu khi chạy chương trình đã được biên soạn. Hàm này thường được gọi là điểm vào vì nó là nơi chạy vào chương trình. 

Ngược lại, Python không có một hàm riêng đóng vai trò như điểm vào của một tập lệnh. Bạn có thể thực sự đặt cho hàm điểm vào trong một tập lệnh Python bất cứ cái tên nào bạn muốn!

Mặc dù Python không gán bất kỳ ý nghĩa nào cho hàm main(), best practice là đặt tên cho hàm điểm vào main() bằng mọi cách. Bằng cách đó, bất kỳ một lập trình viên nào đọc được tập lệnh của bạn cũng ngay lập tức biết hàm này là điểm vào của code hoàn thành nhiệm vụ chính của tập lệnh. 

Thêm nữa, main() nên gồm bất cứ code nào mà bạn muốn hoạt động khi trình thông dịch Python chạy tệp. Điều này tốt hơn là đặt code trực tiếp trong khối điều kiện vì một người dùng có thể dùng lại main() nếu họ import module của bạn. 

Thay đổi tệp best_practices.py do đó nó sẽ trông như đoạn code dưới đây: 

Python

from time import sleep

print("This is my file to demonstrate best practices.")

def process_data(data):
    print("Beginning data processing...")
    modified_data = data + " that has been modified"
    sleep(3)
    print("Data processing finished.")
    return modified_data

def main():
    data = "My data read from the Web"
    print(data)
    modified_data = process_data(data)
    print(modified_data)

if __name__ == "__main__":
    main()

Trong ví dụ này, bạn thêm định nghĩa của main() bao gồm đoạn code trước đó vào khối điều kiện. Sau đó bạn thay đổi khối điều kiện nên nó chạy main(). Nếu bạn chạy đoạn code này như một tập lệnh hoặc import nó, bạn sẽ nhận được cùng một kết quả như phiên hoạt động trước đó. 

Gọi các hàm khác từ main()

Một hoạt động phổ biến khác trong Python là có main() chạy các hàm khác, thay vì gồm code hoàn thành nhiệm vụ trong main(). Điều này đặc biệt hữu ích khi bạn có thể soạn tác vụ nhìn chung của mình từ các tác vụ phụ nhỏ hơn có thể chạy độc lập.

Ví dụ, bạn có một tập lệnh thực hiện như sau:

  1. Đọc tệp dữ liệu từ một nguồn có thể là một nguồn dữ liệu, một tệp trên đĩa hoặc một web API
  2. Xử lý dữ liệu
  3. Ghi các dữ liệu đã được xử lý đến một địa điểm khác

Nếu bạn thực hiện mỗi một tác vụ phụ trong các hàm riêng biệt thì bạn (hoặc người khác) có thể dễ dàng tái sử dụng một vài bước và bỏ qua những bước mà bạn không muốn. Sau đó bạn có thể tạo một quy trình hoạt động mặc định trong main().

Việc chia công việc thành nhiều hàm giúp việc tái sử dụng dễ dàng hơn nhưng lại tăng độ khó cho người khác khi cố gắng diễn giải code của bạn vì họ phải theo dõi nhiều sự chuyển lệnh trong luồng của chương trình. 

Sau khi đổi tệp best_practices.py thì code của bạn sẽ trông như:

Python

from time import sleep

print("This is my file to demonstrate best practices.")

def process_data(data):
    print("Beginning data processing...")
    modified_data = data + " that has been modified"
    sleep(3)
    print("Data processing finished.")
    return modified_data

def read_data_from_web():
    print("Reading data from the Web")
    data = "Data from the web"
    return data

def write_data_to_database(data):
    print("Writing data to a database")
    print(data)

def main():
    data = read_data_from_web()
    modified_data = process_data(data)
    write_data_to_database(modified_data)

if __name__ == "__main__":
    main()

Trong đoạn code ví dụ này, 10 dòng đầu tiên có cùng nội dung mà chúng có trước đó. Định nghĩa hàm thứ hai ở dòng 12 tạo và trả về một số mẫu dữ liệu, và định nghĩa hàm thứ ba ở dòng 17 mô phỏng việc ghi dữ liệu đã được sửa đổi vào cơ sở dữ liệu. 

Ở dòng 21, main() được xác định. Trong ví dụ này, bạn đã chỉnh sửa main() để nó lần lượt gọi các hàm đọc dữ liệu, phân tích dữ liệu và ghi dữ liệu. 

Đầu tiên, data được tạo ra từ read_data_from_web(). data được chuyển đến process_data(), trả về modified_data. Cuối cùng, modified_data được chuyển vào write_data_to_database().

Hai dòng cuối của tập lệnh là khối điều kiện kiểm tra __name__ và chạy main() nếu lệnh if là True. 

Bây giờ bạn có thể chạy toàn bộ quá trình xử lý từ dòng lệnh như sau:

Shell

$ python3 best_practices.py
This is my file to demonstrate best practices.
Reading data from the Web
Beginning data processing...
Data processing finished.
Writing processed data to a database
Data from the web that has been modified

Trong output từ việc thực hiện này, bạn có thể thấy rằng trình thông dịch Python đã chạy main(), read_data_from_web(), process_data() và write_data_to_database(). Tuy nhiên bạn còn có thể import tệp best_practices.py và tái sử dụng process_data() cho một nguồn dữ liệu đầu vào khác giống như sau: 

Python 

>>> import best_practices as bp
This is my file to demonstrate best practices.
>>> data = "Data from a file"
>>> modified_data = bp.process_data(data)
Beginning data processing...
Data processing finished.
>>> bp.write_data_to_database(modified_data)
Writing processed data to a database
Data from a file that has been modified

Trong ví dụ này, bạn đã import best_practices và rút ngắn tên thành bp cho đoạn code này. 

Quá trình import khiến trình thông dịch Python thực hiện tất cả các dòng lệnh code trong tệp best_practices.py, do đó output hiển thị dòng giải thích mục đích của tệp. 

Sau đó, bạn lưu trữ dữ liệu từ một tệp trong data thay vì đọc dữ liệu từ web. Thêm nữa bạn sử dụng lại process_data() và write_data_to_database() từ tệp best_practices.py. Trong trường hợp này, bạn đã tận dụng việc sử dụng lại code của mình thay vì xác định tất cả hàm logic trong main().

Tóm tắt các best practice hàm chính trong Python

Đây là bốn best practice chính về main() trong Python mà bạn mới thấy:

  1. Đặt code mất nhiều thời gian chạy hoặc có hiệu ứng khác trên máy tính trong một hàm hoặc class, nên bạn có thể kiểm soát chính xác khi code đó được thực hiện. 
  2. Sử dụng các giá trị khác của __name__ để xác định ngữ cảnh và thay đổi cách chuyển vận của code với một câu lệnh điều kiện. 
  3. Bạn nên đặt tên hàm điểm vào là hàm main() để truyền đạt ý định của hàm, mặc dù Python không gán bất kỳ ý nghĩa đặc biệt nào cho một hàm có tên main().
  4. Nếu bạn muốn dùng lại hàm từ code của mình, xác định logic trong các hàm bên ngoài hàm main() và gọi các hàm đó trong hàm main().

Kết luận

Xin chúc mừng! Bây giờ bạn đã biết cách tạo hàm main() trong Python. 

Bạn đã học theo những điều sau:

  • Biết giá trị của biến __name__ rất quan trọng để viết code phục vụ mục đích kép của tập lệnh và module. 
  • __name__ nhận các giá trị khác nhau phụ thuộc vào cách mà bạn chạy tệp Python. __name__ sẽ bằng với:
    - "__main__" khi tệp được chạy từ dòng lệnh hoặc với python -m (để chạy một tệp __main__ của package)
    - Tên của module nếu module đó đang được import. 
  • Các lập trình viên Python đã phát triển một tập các good practice để dùng khi bạn muốn phát triển code có thể sử dụng lại.

Bây giờ bạn có thể sẵn sàng để viết một số code hàm main() Python rồi!