Reportar esta app

Description

Module và package là những cấp độ quản lý code cao hơn trong Python. Module cho phép lưu trữ hàm ( và code khác) trên các file riêng rẽ để sau tái sử dụng trong các file và dự án khác. Package cho phép nhóm các module lại với nhau. Sử dụng module và package giúp bạn dễ dàng quản lý code trong những chương trình lớn cũng như tái sử dụng code về sau.

Ví dụ sử dụng module trong Python.

Hãy bắt đầu với một ví dụ minh hoạt. Tạo hai file my_math.py và main.py trong cùng 1 thư mục và viết code như sau:

my_math.py:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
print('--- start of my_math ---')

PI = 3.14159
E = 2.71828
message = 'My math module'

def sum(start, *numbers):
    '''Calculate the sum of unlimited number
    Params:
        start:int/float, the start sum
        *numbers:int/float, the numbers to sum up
    Return: int/float
    '''

    for x in numbers:
        start += x
    return start

def sum_range(start, stop, step=1):
    '''    Calculate the sum of intergers
    Params:
        start:int, start range number
        stop:int, stop range number
        step:int, the step between value
    Returns: int
    '''

    sum =
    for i in range(start, stop, step):
        sum += i
    return sum

def fact(n):
    '''Calculate the factorial of n
    Params:
        n:int
    Return: int
    '''

    p = 1
    for i in range(1, n + 1):
        p *= i
    return p

print('--- start of my_math ---')

main.py:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
import my_math

print(my_math.message)

sum = my_math.sum(, 1, 2, 3, 4)
sum_range = my_math.sum_range(1, 10)
fact = my_math.fact(3)

print('sum = ', sum)
print('sum_range = ', sum_range)
print('fact = ', fact)

print('Pi = ', my_math.PI)
print('e = ', my_math.E)

Bạn cần chạy chương trình từ file main.py. Kết quả thu được như sau:

File my_math.py là một file script Python hoàn toàn bình thường. Trong file này định nghĩa một số hàm tính toán (sum,sum_range, fact) và một số biến (Pi,E, greeting).

Điểm khác biệt của file này là chỉ chứa định nghĩa hàm nhưng không sử dụng gọi hàm.

Trong file main.py chúng ta sử dụng các hàm đã xây dựng từ my_math.py.

Để ý lệnh import my_math ở đâu file. Đây là lệnh yêu cầu Python tải nội dung của file my_math.py khi chạy file script main.py.

my_math và main là hai module trong Python.

Module trong Python.

Trong Python, bất kỳ file script đều được gọi là một module. Khi bạn tạo một file code Python mới, bạn đang tạo một module. Như vậy, ngay từ những bài học đầu tiên bạn đã xây dựng module Python.

Tên module là tên file. Trong ví dụ ở phần trên, Module my_math viết trong file my_math.py. Module main viết trong file main.py.

Mỗi module đều có thể được tải và thực thi bởi Python interpreter. Như ở trên, bạn có thể chạy module my_math và mamin riêng rẽ vì chúng đều là file code Python hợp lệ.

Tuy nhiên, nếu bạn chạy module my_math thì sẽ không ra kết quả gì vì trong module này chúng ta chỉ định nghĩa các hàm và biến chứ không hề sử dụng chúng. Đây là điểm khác biệt giữa my_math và main: trong my_match chỉ chứa khai báo (hàm/biến), trong main chứ lời gọi.

Như vậy cách thứ xây dựng và sử dụng làm cho các module khác biệt nhau. Có những module được xây dựng ra với vai trò cung cấp thư viện hàm cho module khác sử dụng. Có những module sử dụng hàm định nghĩa trong các module khác.

Tham khảo thêm ngay  Property (thuộc tính) trong Python

Để sử dụng hàm/ biến/ kiểu dữ liệu định nghĩa trong file/module khác. Python sử dụng lệnh import. Có hai kiểu viết lệnh import khác nhau:

  1. import<module 1>, <module 2>, <module 3>…..
  2. form <module> import < tên 1>, <tên 2>,…

Sử dụng module với import…

Trong ví dụ minh hoạt chúng ta đã sử dụng lối viết này:

import my_math

Cách viết này sẽ tải toàn bộ module my_math, gần giống hệ với việc copy nội dung của my_math.py đặt vào vị trí của lệnh import my_math và chạy chương trình. Điều này có nghĩa là tất cả các lệnh trong module đều được thực hiện khi import.

Bạn có thể thấy điều này khi cặp lệnh print() viết ở đầu và cuối module my_math đều được thực hiện.

Cách này sẽ chỉ tải module khi gặp lệnh import lần đầu tiên. Khi gặp lệnh import lần thứ 2, Python sẽ không tải module nữa. Bạn có thể thấy rất rõ điều này khi căp lệnh print() ở đầu và cuối module my_math chỉ được gọi một lần duy nhất trước khi bắt đầu module main mặc dù chúng ta viết lệnh import my_math hai lần.

Hãy để ý cách chúng ta gọi hàm và sử dụng biến ( được khai báo trong module my_math):


1
2
3
4
5
6
sum = my_math.sum(, 1, 2, 3, 4)
sum_range = my_math.sum_range(1, 10)
fact = my_math.fact(3)
print('Pi = ', my_math.PI)
print('e = ', my_math.E)
print(my_math.message)

Tức là thay vì viết trực tiếp tên hàm/ biến, bạn phải cung cấp thêm tên module my_math. Cấu trúc chung để sử dụng một hàm/biến trong module khác là tên_module.tên_hàm() và tên_module.tên_biến.

Bạn có thể sử dụng alias như lệnh import như sau:


1
import my_math as mm

Tên mm trong lệnh trên được gọi là alias ( tên giả ) của my_math. Khi này thay vì sử dụng tên module my_math bạn có thể sử dụng alias mm như sau:


1
2
3
print(mm.message)
print(mm.sum(1, 2, 3, 4, 5))
print(mm.E)

Nghĩa là bạn có thể sử dụng alias thay cho tên module. Bạn có thể đồng thời sử dụng cả tên module lẫn alias.

Alias rất tiện lợi khi tên module quá dài hoặc tên module trùng lặp với một hàm/ biến có sẵn của Python.

Sử dụng module với form…import…

Giờ hãy xóa hoặc comment toàn bộ code của main.py và viết lại code mới như sau:


1
2
3
4
5
from my_math import sum_range, fact, message

print(message)
print('sum_range = ', sum_range(1, 100, 2))
print('5! = ', fact(5))

Khi cấu trúc form…import bạn có thể thấy rằng Python vẫn import toàn bộ code của module my_math. Điều này thể hiện qua việc hai lệnh pirnt() ở đầu và cuối module my_math đều được thực hiện ở vị trí gọi from…import.

Tuy nhiên, giờ đây bạn không thể sử dụng được tất cả các hàm/ biến của module my_math như trước được nữa. Giờ bạn chỉ có thể sử dụng hàm sum_range(), fact() và biến message.

Điều đặc biệt là bạn có thể sử dụng tên ngắn gọn của các đối tượng này (không cần tên module), giống hệ như khi các đối tượng này được khai báo trực tiếp trong module main:


1
2
3
print(message)
print('sum_range = ', sum_range(1, 100, 2))
print('5! = ', fact(5))

Bạn cũng có thể chỉ định alias cho từng tên gọi trong lệnh from…import như sau:


1
2
3
4
5
from my_math import sum_range as sr, fact as f, message as msg

print(msg)
print('sum_range = ', sr(1, 100, 2))
print('5! = ', f(5))

Ở đây khi import chúng ta chỉ định alias sr cho hàm sum_range(), f cho hàm fact(), msg cho biến message. Trong code sau đó chúng ta có thể sử dụng alias thay cho tên thật.

Lưu ý, nếu bạn đã đặt alias thì không thể sử dụng tên thật của đối tượng được nữa. Tức là không thể đồng thời sử dụng alias và tên thật.

Bạn cũng có thể sử dụng cách import như sau:


1
from my_math import *

Tuy nhiên đây là cách không khuyến khích. Nó dễ dàng làm rối không gian tên của module hiện tại nếu có quá nhiều tên gọi được import.

Tham khảo thêm ngay  Cú pháp Python cơ bản

Thực thi module.

Khi xây dựng một module bạn có thể phải dự phòng hai tình huống:

  1. Module đó được thực thi trực tiếp
  2. Module được import vào một module khác, tạm gọi là thực thi gián tiếp

Mỗi module có một thuộc tính đặc biệt là _name_. Lư ý có hai dấu _ trước và sau name. Bạn có thể thử in ra qua lệnh print(_name__).

Khi một moduel được thực thi trực tiếp, _name_ sẽ được gán giá trị ‘_main_’. Nếu khi thực thi một module mà _name_==’_main_’ thì đây là module chủ của chương trình.

Khi một module được thực thi gián tiếp, _name_ của nó sẽ có giá trị là tên module ( cũng là tên file bỏ đi phần mở rộng py).

Với đặc thù trên, khi xây dựng module trong Python mà cần chạy ở cả hai kiểu (trực tiếp và gián tiếp) người ta thường sử dụng pattern sau đây:

  1. Mỗi module sẽ có một hàm main() chứa những lệnh cần thực thi theo kiểu trực tiếp
  2. Kiểm tra nếu _name_==’_main_’ thì thực thi main()

1
2
3
4
5
6
def main():
    'Thực thi các logic của chương trình'
    # code cần chạy

if __name__ == '__main__':
    main()

Khi gián tiếp qua import _name_ sẽ không thể có giá trị _main_, vì vậy logic của main() sẽ không thực thi.

Khi một module được chạy gián tiếp qua import, Python cũng thực hiện lưu tạm bản dịch bytecode của nó trong thư mục _pycache_. Thực mục _pycache_ nằm cùng thư mục với module được import. Ví dụ, với module my_math, khi được import, Python sẽ tạo ra file _pycache_/my_math.cpython-38.pyc (nếu sử dụng Python 3.8). Nếu bạn có nhiều phiên bản Python khác nhau, khi chạy với phiên bản nào thì pyc sẽ có tên tương ứng.

Nhắc lại: khi chạy một script, Python sẽ dịch nó thành bytecode trung gian. Chương trình máy ảo của Python sẽ tiếp tục dịch bytecode thành mã máy để thực thi trên từng hệ điều hành.

File bytecode của Python có phần mở rộng là pyc.

Khi import một module cũng xảy ra quá trình dịch bytecode như vậy. Tuy nhiên, Python sẽ lưu lại file pyc để lần sâu không cần dịch lại. Việc lưu trữ file pyc giúp giảm thời gian tải một chương trình.

Lưu ý, khi trực tiếp thực thi một module Python sẽ không lưu lại file bytecode.

File bytecode không thực thi nhanh hơn. Nó chỉ giảm thời gian tải ứng dụng.

Vì lý do này, khi xây dựng chương trình bằng Python bạn nên tận dụng việc xây dựng và import module, thay vì code trực tiếp trong một module chính lớn. Toàn bộ code nên đưa về các module và để module chính đơn giản nhất có thể.

Vấn đề đường dẫn khi sử dụng module.

Bạn có thể thấy khi import module bạn không hề chỉ định đường dẫn đến file script. Python thực hiện việc tìm kiếm file module tự động theo các thư mục lưu trong biến sys.path.

Biến sys.path la fmootj danh sách lưu những đường dẫn được đăng ký trong Python. Bạn có thể xem nội dung của sys.path như sau:


1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import sys
>>> sys.path # đây là một list
['.', 'E:\\OneDrive\\TuHocICT\\Learn Python\\PythonApps\\HelloPython', 'F:\\Users\\thang\\AppData\\Local\\Programs\\Python\\Python38\\python38.zip', 'F:\\Users\\thang\\AppData\\Local\\Programs\\Python\\Python38\\DLLs', 'F:\\Users\\thang\\AppData\\Local\\Programs\\Python\\Python38\\lib', 'F:\\Users\\thang\\AppData\\Local\\Programs\\Python\\Python38', 'F:\\Users\\thang\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages']
>>> for p in sys.path:
...     print(p)
...
.
E:\OneDrive\TuHocICT\Learn Python\PythonApps\HelloPython
F:\Users\thang\AppData\Local\Programs\Python\Python38\python38.zip
F:\Users\thang\AppData\Local\Programs\Python\Python38\DLLs
F:\Users\thang\AppData\Local\Programs\Python\Python38\lib
F:\Users\thang\AppData\Local\Programs\Python\Python38
F:\Users\thang\AppData\Local\Programs\Python\Python38\lib\site-packages

Bạn có thể để ý ngay đường dẫn đầu tiên chính là thư mục làm việc nơi bạn đặt file module chính. Ví dụ nếu bạn chạy module main.py và trong main.py có lệnh import my_math thì Python trước hết sẽ tìm my_math.py trong thư mục chứ main.py.

Tham khảo thêm ngay  Decorator ( hàm trang trí) trong Python

Nếu không tìm thấy file tương ứng, Python sẽ lần lượt tìm trong các thư mục còn lại.

Để ý một tình huống khác. Giả sử bạn có module mysql nằm trong file mysql.py nằm trong thư mục con modules\database\ của thư mục hiện hành (tức là đường dẫn tương đối tới file là modules\database\mysql.py).

Để import module mysql bạn cần viết như sau: import modules.database.mysql. Tức là bạn cần chỉ định cấu trúc đường dẫn tương đối tới module. Sự khác biệt ở chỗ các phần của đường dẫn được phân tách bởi dấu chấm. Python sẽ tự động ghép đường dẫn này với các đường dẫn lưu trong sys.path.

Nếu trong trường hợp các file module của bạn nằm ở một nơi khác, bạn phải có cách chỉ định đường dẫn để Python có thể tìm thấy.

Cách đơn giản nhất như sau:


1
2
3
4
import sys
sys.path.append('-- new path --') # thêm thư mục mới vào sys.path
# giờ sẽ import module
import a_module

Logic ở đây rất đơn giản. Do sys.path chỉ là một list, bạn có thể tự thêm đường dẫn mới vào sys.path. Khi đó Python sẽ tìm kiếm cả trong đường dẫn bạn thêm vào.


1
2
3
4
5
>>> import sys
>>> sys.path.append('E:\OneDrive\TuHocICT\Learn Python\Modules')
>>> import sample_module
hello world from sample module
>>>

Sử dụng package trong Python

Trong Python, một số module trong cùng thư mục có thể kết hợp laijd dể tạo ra một package. Package giúp đơn giản hóa hơn việc sử dụng nhiều module có liên quan.

Hãy cùng thực hiện ví dụ sau:

Tạo thư mục my_package. Trong thư mục này tạo hai file module1.py và module2.py với code như sau:

module1.py


1
2
3
4
print('This is the module 1')

def func1():
    print('Module 1 - func 1')

module2.py


1
2
3
4
print('This is the module 2')

def func2():
    print('Module 2 - func 2')

Nếu dừng lại ở đây bạn sẽ có hai module riêng rẽ. Khi sử dụng bạn cần import từng module.

Giờ hãy tạo file__init__.py trong thư mục my_package. Viết code như sau cho __init__.py:


1
2
3
4
5
6
from my_package.module1 import *
from my_package.module2 import *

#hoặc cũng có thể import như thế này:
#import my_package.module1
#import my_package.module2

Đây là hai lệnh import thông thường mà bạn đã học ở phần trên. Bạn có thể sử dụng kiểu import nào cũng được. Tuy nhiên cần lưu ý về cách viết module: my_package.module1. Bạn cần chỉ định tên thư mục, nếu không Python sẽ không tìm ra được module tương ứng.

Tên file__init__.py có ý nghĩa đặc thù trong Python. Nếu Python nhìn thấy thư mục nào có file với tên gọi__init__.py nó sẽ tự động coi thư mục này là một package, chứ không còn là thư mục bình thường nữa.

Bên ngoài thư mục package hãy tạo module main.py và viết code như sau:


1
2
3
4
5
6
7
import my_package as mp

mp.module1.func1()
mp.module2.func2()

from my_package import *
module1.func1();

Bạn có thể thấy ngay lệnh import giờ không hoạt động với từng module nữa mà là với tên thư mục my_package.

Tất cả các thư mục chứ file __init__.py sẽ trở thành một package. Tên thư mục trở thành tên package và có thể sử dụng cùng lệnh import hoặc form/import.

Khi import một package bạn sẽ đồng thời import tất cả các module của nó

Nếu sử dụng lệnh import<tên_package> bạn truy cập vào các hàm của từng module qua cú pháp <tên_package>.<tên_module>.<tên_hàm>().

Nếu sử dụng form <tên_package> import <tên_hàm> bạn có thể sử dụng tên ngắn gọn của hàm như bình thường.

Lưu ý trong __init__.py và trong module sử dụng package nên dùng cùng một cách import. Ví dụ cùng dùng import hoặc cùng dùng from…import.

Leave a Reply

Your email address will not be published. Required fields are marked *