Cộng đồng chia sẻ tri thức Lib24.vn

Giới thiệu module trong Python

Gửi bởi: Phạm Thọ Thái Dương 20 tháng 11 2020 lúc 15:40:38


Mục lục
* * * * *

Giới thiệu module trong Python

Module chỉ đơn giản là những file Python thôi và dĩ nhiên việc tạo file Python không còn gì xa lạ và khó khăn với chúng ta nữa. Bạn không cần phải có một dòng code đặc biệt trong một file Python để biến nó thành một module, chỉ đơn giản là một file Python bình thường như mọi khi bạn vẫn tạo, thậm chí file Python đó chẳng ghi bất cứ chữ nào thì vẫn là một module.

Tuy nhiên, việc đặt tên cho module có một chút ràng buộc. Bản thân khi muốn sử dụng module thì ta sẽ lưu module đó vào một biến, biến đó thuộc lớp Module. Mà đã là biến, thì phải tuân theo những quy tắc đặt tên biến, và chỉ vậy thôi!

Module của Python có thể không nhất thiết phải là file Python mà có thể là những file được viết bởi những ngôn ngữ lập trình khác như C, C++,… Ví dụ như (Java – Jython). Những module như vậy được gọi là extension module, và thường được sử dụng cho việc lưu các external library. Tuy nhiên phạm vi bài viết này sẽ không đề cập đến extension module, cho nên bạn không cần lo lắng nếu không hiểu những ngôn ngữ lập trình vừa mới đề cập ở trên.

Và trong bài viết này, Kteam sẽ comment tên module ở ngay đầu mỗi file để các bạn có thể dễ dàng theo dõi, ví dụ như đây là module có tên a.py.

Lưu ý: Các module được tạo cùng một thư mục.

# a.py

text = "How Kteam"

def pout(name):
    print("Hello", name)

pout(text)
123456789

Câu lệnh import

Đây là câu lệnh cơ bản nhất khi làm việc với các module Python. Đầu tiên ta có một module như thế này.

# a.py

print("imported")

def say(something):
    print(something)

Và một main module, chúng ta sẽ chạy module này.

# main.py

import a

a.say("Đây là file main")
a.say("Chạy bởi hàm trong module a")
1234567

Kết quả:

imported
Đây là file main
Chạy bởi hàm trong module a
1234

Đơn giản đúng không nào. Ta chỉ cần import <tên module>, khi đó file module sẽ chạy và tạo một module object lưu dưới một biến với tên là biến đó. Module object này có các attribute và method lần lượt là các biến và hàm (với scope global). Cụ thể như sau:

# a.py

print("imported")

var_module_a = 10

def func_module_a():
    local_var = 20
    print("run function of module a")
12345678910
# main.py

import a

print(type(a))

print(a.var_module_a)
a.func_module_a()
print(a.local_var)
12345678910

Kết quả:

imported
<class 'module'>
10
run function of module a
AttributeError: module 'a' has no attribute 'local_var'
123456

Một điều nữa là, ta có thể import nhiều module cùng một lúc trên cùng một dòng lệnh.

import module_a, module_b, module_c

Tuy nhiên, việc này không khuyến khích vì không rõ ràng. Thường ta sẽ:

import module_a
import module_b
import module c 
1234

Tuy nhiên việc import các module cách nhau bởi dấu phẩy (,) được sử dụng rất nhiều trong các  trường hợp và sẽ được đề cập phần sau.

Câu lệnh from import

Sử dụng lại module a.py như phần trước:

# main.py

from a import func_module_a

func_module_a() # không cần phải sử dụng module object
123456

Kết quả:

imported
run function of module a
123

Câu lệnh này đã copy hàm func_module_a trong lưu vào biến func_module_a được tạo trong module main.py. Nôm na nó sẽ tương tự như khi bạn làm như sau:

# main.py

import a

func_module_a = a.func_module_a

del a # xóa biến a

func_module_a()
12345678910

Để thấy rõ hơn việc copy này, Kteam lại tiếp tục hẹn bạn ở phần sau của bài viết.

Đôi khi ta muốn import nhiều hơn một attribute hay method thì sao, đây chính là lúc dấu phẩy (,) trong import được phát huy tác dụng thực sự

# main.py

from a import var_module_a, func_module_a

print(var_module_a)
func_module_a()
1234567

Kết quả:

imported
10
run function of module a
1234

Ở hai trường hợp trên, ta chỉ import một hoặc hai thứ trong module a thôi. Thế nếu ta muốn import hết tất cả thì sao?

Giả sử module a có một tỷ function thì việc sử dụng dấu phẩy (,) gần như là bất khả thi. Vậy thì ta sử dụng import. Đúng! Tuy nhiên đặt một vấn đề nữa là khi sử dụng import thì ta đã tạo một module object, mọi attribute và method đều phải được thông qua object đó.

Có cách nào vừa import hết như import nhưng lại không cần phải sử dụng thông qua module object như câu lệnh from vừa làm như trên? Có đấy, có một cách giúp bạn import hết tất cả các thứ.

# main.py

from a import *

print(var_module_a)

func_module_a()
12345678

Nếu như bạn không qua xa lạ với việc xử lí file trên máy tính, thì kí tự * chính là kí tự kí hiệu cho tất cả.

Lưu ý: Ở Python 2.X, câu lệnh from module import * có thể sử dụng ở trong một function, tuy nhiên điều này không được xảy ra ở Python 3.X.

Import một module nhiều lần

Sử dụng import hay from import để import một module thì chỉ hoạt động một lần. Nhìn chung, việc import được coi là expensive operation. Vậy nên Python chỉ thực hiện mỗi file một lần, khi thấy file đã được import thì khi đó bạn có gõ lại import không khác gì lắm việc bạn gõ một dòng comment. Bạn sẽ thấy rõ thông qua ví dụ sau đây:

# a.py

print("imported")

var_module_a = 10
123456
# main.py

import a

print(a.var_module_a)

a.var_module_a = 100

import a

print(a.var_module_a)
123456789101112

Kết quả:

imported
10
100
1234

Như bạn thấy, dòng imported chỉ hiện một lần dù chúng ta gõ tới 2 dòng lệnh import a. Và biến của module object sau khi chúng ta gán lại giá trị cũng không bị reset trở lại là 10.

Tuy nhiên khi sử dụng from import rồi sau đó sử dụng import thì lần import thứ 2 được coi như là một cách reset lại các giá trị của biến thuộc module đó.

# main.py

from a import var_module_a

var_module_a = 100

import a

print(a.var_module_a)
12345678910

Kết quả:

imported
10
123

Vì bản thân khi sử dụng from import là ta copy, chỉ lấy giá trị. Tuy vậy vẫn có một số ngoại lệ sẽ được đề cập ở phần tiếp theo.

Lưu ý: việc reset này chỉ hoạt động khi bạn from import sau đó import, còn khi import rồi tiếp tục import thì sẽ không được kết quả như trên.

Lưu ý về list object khi import

Nhắc lại một tính chất của list object, ta có thể thay đổi giá trị của object đó thông qua một object khác.

Ví dụ:

>>> lst = [1, 2, 3]
>>> a = lst
>>> a[0] = 10
>>> lst
[10, 2, 3]
123456

Quay lại nội dung của chúng ta, đầu tiên ta sẽ thay đổi module a.py một chút và thử một số thứ hay ho khi chạy main.py

# a.py

num = 20
lst = [1, 2, 3]
12345
# main.py

from a import num, lst

num = 200
lst[0] = 100

import a

print(a.num)
print(a.lst)
123456789101112

Kết quả:

20
[100, 2, 3]
123

Bạn thấy đó, mặc dù sử dụng from import tạo copy sau đó sử dụng thêm import để reset lại giá trị các biến, tuy nhiên với các object như list thì việc này không được. Vì nhìn chung, khi gán các giá trị như list là ta gán địa chỉ. Để hiểu thêm về việc gán này, bạn có thể tham khảo bài viết KIỂU DỮ LIỆU LIST TRONG PYTHON - PHẦN 1

Reload module

Python chỉ cho phép ta import một module một lần, không tự động reload, không có nghĩa là chúng ta không thể reload lại module. Ta có thể sử dụng hàm reload trong thư viện của Python.

# a.py

var = 10
1234
# main.py

import a

a.var = 20
print(a.var)

import a
print(a.var)

from importlib import reload

reload(a)
print(a.var)
123456789101112131415

Kết quả:

20
20
10
1234

Lưu ý: Về thư viện cho hàm reload thì nếu bạn sử dụng Python 3.4+ thì thư viện như trên. Nếu không thì bạn sử dụng thư viện imp. Với Python 3.4+ tới 3.7.4 thì bạn vẫn còn có thể sử dụng hàm reload trong thư viện imp, tuy nhiên sẽ có báo warning và không khuyến khích.

Đổi tên module, attribute khi import

Đôi khi, tên của module hay attribute của chúng ta rất dài. Chúng ta có thể thay đổi lại tên của module hoặc attribute đó, tuy nhiên đôi lúc những thư viện chuẩn chúng ta cần import thì việc thay đổi những điều đó xem như là một việc làm khá nguy hiểm nếu bạn không nắm rõ bạn đang làm cái gì. Có một cách đơn giản hơn đó là sử dụng as.

# a_long_name_for_a_module.py

short_name = 10

a_long_name_for_a_variable = 20

def a_long_name_for_a_function():
    print("How Kteam")
123456789
# main.py

import a_long_name_for_a_module as mdule
from a_long_name_for_a_module import a_long_name_for_a_variable as longvar,\
                a_long_name_for_a_function as longfunc

print(mdule.short_name)
print(longvar)
longfunc()
12345678910

Kết quả:

10
20
How Kteam
1234

Lưu ý: Bạn để ý một điều là, mặc dù ta đã đổi tên module ban đầu thành module rồi, tuy nhiên câu lệnh import tiếp theo vẫn phải sử dụng tên gốc vì khi đổi tên lại thì tên đó chỉ có giá trị trong file chạy, còn khi Python tìm module thì tìm ngoài thư mục, không liên quan gì nhau.

Trường hợp bắt buộc dùng import

Giả sử như bạn có hai module có một hoặc vài biến hoặc hàm cùng tên, khi đó nếu như bạn sử dụng from import, ví dụ như:

from a import var

from b import var

Thì khi đó, biến var này sẽ có giá trị của module b.

Những trường hợp này, sử dụng import là cách duy nhất tránh tình trạng này, vì khi dùng import thì ta sẽ tạo được hai module object. Dĩ nhiên là các object có thể có cùng attribute, nhưng ta vẫn có thể sử dụng được chúng một cách độc lập.

import a
import b

print(a.var)
print(b.var)
123456

Folder __pycache__

Nếu bạn nhìn lại thư mục của bạn, mình tin rằng sẽ có một folder tên là __pycache__, bên trong chứ những file .pyc (đôi lúc là .pyo). Những file này là những file bạn muốn import được biên dịch (compile) ra.

Nhìn chung thì bạn không cần quan tâm lắm tới những file này, mục đích của những file này sẽ giúp code của bạn sẽ chạy nhanh hơn từ lần thứ hai trở đi (bạn sẽ thấy rõ điều này khi import những thư viện lớn), vì lần đầu khi chạy bận biên dịch những file bạn import, còn lần hai thì việc biên dịch không còn diễn ra nữa.

Nếu bạn không muốn việc này, thì mỗi lần chạy file Python, bạn sử dụng command sau đây:

python -B file.py1

Một cách khác nữa là bạn thay đổi giá trị của biến môi trường (environment variable) PYTHONDONTWRITEBYTECODE (Python don’t write byte code) thành một giá trị khác rỗng.


Được cập nhật: 25 tháng 3 lúc 21:32:48 | Lượt xem: 1541

Các bài học liên quan