Phạm vi của biến trong Python – Lý thuyết, bài tập

I. Lý thuyết trọng tâm

1. Biến cục bộ – Biến khai báo bên trong hàm

Khi một biến được tạo ra bên trong thân hàm, nó chỉ “sống” trong phạm vi hàm đó. Bên ngoài hàm, biến này không tồn tại và nếu cố truy cập sẽ gặp lỗi NameError.

Điều này áp dụng cho cả tham số của hàm: khi truyền giá trị vào hàm, Python tạo ra bản sao cục bộ. Mọi phép gán lại bên trong hàm chỉ tác động lên bản sao, không ảnh hưởng biến gốc bên ngoài.

Ví dụ minh hoạ:

>>> def func(a, b):
        n = 10
        a = a * 2
        b = a + b
        return a + b + n

>>> a = 1
>>> b = 2
>>> func(a, b)
16
>>> a, b
(1, 2)
>>> n
NameError: name 'n' is not defined

Phân tích:

  • Bên trong func, các biến n, a, b là biến cục bộ. Dù ab trùng tên với biến bên ngoài, chúng hoàn toàn độc lập.
  • Sau khi hàm kết thúc, a vẫn là 1, b vẫn là 2 – không bị thay đổi.
  • Gọi n ở ngoài hàm → lỗi, vì n chỉ tồn tại trong func.

💡 Ghi nhớ: Trong Python, tất cả biến khai báo trong hàm đều mang tính địa phương (cục bộ), không có hiệu lực bên ngoài hàm.

Câu hỏi vận dụng lý thuyết:

Cho đoạn lệnh:

>>> a, b = 1, 2
>>> def f(a, b):
        a = a + b
        b = b * a
        return a + b

Giá trị của a, b bên ngoài hàm sau khi gọi f(1, 2) hoặc f(10, 20)?

Đáp án: Dù gọi f(1, 2) hay f(10, 20), giá trị a, b bên ngoài luôn là 1, 2a, b trong hàm f là biến cục bộ.

Có thể khai báo biến trong hàm trùng tên với biến ngoài hàm không?

Đáp án: Có thể. Hai biến trùng tên nhưng nằm ở phạm vi khác nhau thì hoàn toàn độc lập, không ảnh hưởng lẫn nhau.

2. Biến toàn cục – Biến khai báo bên ngoài hàm

Biến được tạo ở chương trình chính (ngoài mọi hàm) có phạm vi rộng hơn. Tuy nhiên, cách hàm tương tác với biến này có hai tình huống khác nhau:

Tình huống 1: Hàm có thể ĐỌC giá trị biến toàn cục

Nếu bên trong hàm chỉ sử dụng giá trị của biến (không gán lại), Python cho phép truy cập bình thường.

>>> def f(a, b):
        return a + b + N

>>> N = 10
>>> f(1, 2)
13

Hàm f truy cập được giá trị N = 10N khai báo bên ngoài.

Tình huống 2: Hàm KHÔNG THỂ thay đổi biến toàn cục (mặc định)

Nếu bên trong hàm có phép gán cho biến trùng tên với biến ngoài, Python tự động coi đó là biến cục bộ mới – biến ngoài không bị ảnh hưởng.

>>> def f(n):
        t = n + 1
        return t

>>> t = 10
>>> f(5)
6
>>> t
10

Dù hàm f gán t = n + 1 = 6, biến t ngoài hàm vẫn giữ nguyên giá trị 10.

3. Từ khoá global – Cho phép hàm thay đổi biến toàn cục

Khi cần hàm thực sự thay đổi giá trị của biến ngoài hàm, ta khai báo biến đó với từ khoá global ngay bên trong hàm.

Cú pháp:

def tên_hàm():
    global tên_biến
    # thao tác với biến

Ví dụ:

>>> def f(n):
        global t
        t = 2 * n + 1
        return t

>>> t = 10
>>> f(1)
3
>>> t
3

Sau khi gọi f(1), biến t ở chương trình chính đã thay đổi từ 10 thành 3.

💡 Nguyên tắc: Biến ngoài hàm chỉ có thể đọc bên trong hàm. Muốn ghi/thay đổi thì phải dùng global.

Câu hỏi vận dụng lý thuyết:

Cho hàm:

>>> def f(x, y):
        a = 2 * (x + y)
        print(a + n)

Kết quả in ra khi thực hiện:

n = 10
f(1, 2)

Đáp án:

  • $a = 2 \times (1 + 2) = 6$
  • Hàm truy cập biến n = 10 từ bên ngoài
  • Kết quả in ra: $6 + 10 = \textbf{16}$

II. Hướng dẫn thực hành

Nhiệm vụ 1: Lọc phần tử từ danh sách

Yêu cầu: Viết hàm nhận danh sách A chứa các số và một số thực x. Hàm trả lại danh sách B chỉ gồm các phần tử $\geq x$.

Hướng dẫn: Khởi tạo danh sách rỗng B bên trong hàm (biến cục bộ), duyệt A, kiểm tra điều kiện rồi bổ sung bằng append().

def Select(A, x):
    B = []
    for k in range(len(A)):
        if A[k] >= x:
            B.append(A[k])
    return B

Lưu ý phạm vi biến: B là biến cục bộ, chỉ tồn tại trong hàm Select. Sau khi hàm trả về, muốn sử dụng kết quả phải gán vào biến ở chương trình chính, ví dụ: result = Select([3, 1, 5, 2], 2).

Nhiệm vụ 2: Tách từ và chuyển đổi kiểu chữ

Yêu cầu: Viết hàm nhận xâu Str và số c. Tuỳ giá trị c:

  • c = 0: tách từ, chuyển thành chữ IN HOA
  • c = 1: tách từ, chuyển thành chữ in thường
  • c = 2: tách từ, viết hoa kí tự đầu mỗi từ

Các phương thức cần dùng: upper(), lower(), title(), split()

def Tach_tu(Str, c):
    A = Str.split()
    for k in range(len(A)):
        if c == 0:
            A[k] = A[k].upper()
        if c == 1:
            A[k] = A[k].lower()
        if c == 2:
            A[k] = A[k].title()
    return A

Nhiệm vụ 3: Chương trình phân loại số nguyên bằng nhiều hàm

Yêu cầu: Viết chương trình gồm 3 hàm:

  1. Nhập dãy số nguyên (cách nhau bởi dấu cách) → danh sách A
  2. Trích danh sách B gồm các phần tử $> 0$
  3. Trích danh sách C gồm các phần tử $< 0$
def Nhap_Dulieu():
    s = input("Nhập các số nguyên cách nhau bởi dấu cách: ")
    A = s.split()
    for k in range(len(A)):
        A[k] = int(A[k])
    return A

def getB(A):
    B = []
    for x in A:
        if x > 0:
            B.append(x)
    return B

def getC(A):
    C = []
    for x in A:
        if x < 0:
            C.append(x)
    return C

# Chương trình chính
A = Nhap_Dulieu()
print("Danh sách A:", A)
B = getB(A)
C = getC(A)
print("Danh sách B:", B)
print("Danh sách C:", C)

Lưu ý phạm vi biến: Mỗi hàm có biến cục bộ riêng (B trong getB, C trong getC). Chương trình chính nhận kết quả qua giá trị trả về (return) chứ không truy cập trực tiếp vào biến cục bộ.

III. Phương pháp giải các dạng bài tập

Dạng 1: Viết hàm trích xuất và xử lý dữ liệu từ danh sách

Phương pháp chung:

  • Tạo danh sách kết quả rỗng bên trong hàm (biến cục bộ).
  • Duyệt danh sách đầu vào bằng for kết hợp range(len(...)) hoặc duyệt trực tiếp phần tử.
  • Kiểm tra điều kiện → dùng append() để bổ sung phần tử phù hợp.
  • Trả kết quả bằng return. Nếu cần trả nhiều danh sách → return B, C.

Bài mẫu 1 – Luyện tập 1:

Viết hàm nhận danh sách sList gồm các xâu kí tự, trả về danh sách cList gồm kí tự đầu tiên của mỗi xâu tương ứng.

Lời giải:

def lay_ki_tu_dau(sList):
    cList = []
    for s in sList:
        cList.append(s[0])
    return cList

# Ví dụ
print(lay_ki_tu_dau(["Hello", "World", "Python"]))
# Kết quả: ['H', 'W', 'P']

Bài mẫu 2 – Luyện tập 2:

Viết hàm Tach_day() nhận danh sách A, trả về danh sách B gồm các phần tử có chỉ số chẵn và danh sách C gồm các phần tử có chỉ số lẻ.

Lời giải:

def Tach_day(A):
    B = []
    C = []
    for k in range(len(A)):
        if k % 2 == 0:
            B.append(A[k])
        else:
            C.append(A[k])
    return B, C

# Ví dụ
B, C = Tach_day([10, 20, 30, 40, 50])
print("B:", B)  # B: [10, 30, 50]  (chỉ số 0, 2, 4)
print("C:", C)  # C: [20, 40]       (chỉ số 1, 3)

Dạng 2: Viết hàm trả về nhiều giá trị kết hợp thuật toán

Phương pháp chung:

  • Python cho phép hàm trả về nhiều giá trị bằng cú pháp return giá_trị_1, giá_trị_2.
  • Bên ngoài hàm, nhận kết quả bằng: kq1, kq2 = tên_hàm(...).
  • Áp dụng thuật toán phù hợp với bài toán (ví dụ: thuật toán Euclid cho ƯCLN).
  • Tận dụng các công thức liên hệ để tính nhanh (ví dụ: $\text{ƯCLN}(m, n) \times \text{BCNN}(m, n) = m \times n$).

Bài mẫu – Vận dụng 1:

Viết hàm có hai tham số đầu vào m, n. Đầu ra trả lại ƯCLN và BCNN.

Gợi ý: $\text{ƯCLN}(m, n) \times \text{BCNN}(m, n) = m \times n$

Lời giải:

def UCLN_BCNN(m, n):
    a, b = m, n
    while b != 0:
        a, b = b, a % b
    ucln = a
    bcnn = m * n // ucln
    return ucln, bcnn

# Ví dụ
u, b = UCLN_BCNN(12, 18)
print("ƯCLN:", u)  # ƯCLN: 6
print("BCNN:", b)   # BCNN: 36

Giải thích:

  • Dùng thuật toán Euclid: chia lấy dư liên tục cho đến khi b = 0, lúc đó a chính là ƯCLN.
  • Từ công thức: $\text{BCNN} = \dfrac{m \times n}{\text{ƯCLN}}$

Dạng 3: Viết chương trình kiểm tra tính hợp lệ của dữ liệu

Phương pháp chung:

  • Tách chức năng thành hàm riêng: hàm nhập liệu, hàm kiểm tra, chương trình chính điều phối.
  • Xét đầy đủ các trường hợp hợp lệ/không hợp lệ.
  • Với bài toán ngày tháng: cần kiểm tra tháng (1–12), ngày (tuỳ tháng), và xử lý năm nhuận.

Quy tắc năm nhuận:

  • Năm chia hết cho 4 không chia hết cho 100 → nhuận
  • Hoặc năm chia hết cho 400 → nhuận

Bài mẫu – Vận dụng 2:

Viết chương trình nhập day, month, year từ bàn phím (cách nhau bởi dấu cách). Kiểm tra và in ra ngày đó hợp lệ hay không.

Lời giải:

def kiem_tra_ngay(day, month, year):
    if year < 1:
        return False
    if month < 1 or month > 12:
        return False

    # Xác định số ngày tối đa của tháng
    if month in [1, 3, 5, 7, 8, 10, 12]:
        max_day = 31
    elif month in [4, 6, 9, 11]:
        max_day = 30
    elif month == 2:
        # Kiểm tra năm nhuận
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            max_day = 29
        else:
            max_day = 28

    if day < 1 or day > max_day:
        return False
    return True

# Chương trình chính
s = input("Nhập day month year cách nhau bởi dấu cách: ")
parts = s.split()
day = int(parts[0])
month = int(parts[1])
year = int(parts[2])

if kiem_tra_ngay(day, month, year):
    print("Ngày hợp lệ")
else:
    print("Ngày không hợp lệ")

Ví dụ chạy thử:

Đầu vào Kết quả
29 2 2024 Ngày hợp lệ (2024 là năm nhuận)
29 2 2023 Ngày không hợp lệ (2023 không nhuận)
31 4 2025 Ngày không hợp lệ (tháng 4 chỉ có 30 ngày)
15 8 2000 Ngày hợp lệ

IV. Sơ đồ tư duy

PHẠM VI CỦA BIẾN

  1. Biến cục bộ (khai báo trong hàm)
    • 1.1. Chỉ tồn tại và hoạt động bên trong hàm chứa nó
    • 1.2. Hàm kết thúc → biến bị huỷ
    • 1.3. Gọi bên ngoài hàm → lỗi NameError
    • 1.4. Tham số hàm cũng là biến cục bộ
  2. Biến toàn cục (khai báo ngoài hàm)
    • 2.1. Hàm có thể đọc giá trị của biến toàn cục
    • 2.2. Hàm không thể gán/thay đổi biến toàn cục (mặc định)
    • 2.3. Nếu gán trong hàm → Python tạo biến cục bộ mới, biến ngoài không đổi
  3. Từ khoá global
    • 3.1. Cho phép hàm thay đổi giá trị biến toàn cục
    • 3.2. Cú pháp: global tên_biến đặt ở đầu hàm
    • 3.3. Sau đó mọi thay đổi trong hàm đều tác động đến biến ngoài
  4. Trùng tên biến
    • 4.1. Biến trong hàm và biến ngoài hàm cùng tên → hai biến khác nhau
    • 4.2. Biến cục bộ “che” biến toàn cục trong phạm vi hàm
Thầy Phạm Thành Danh

Thầy Phạm Thành Danh

(Người kiểm duyệt, ra đề)

Chức vụ: Trưởng ban biên soạn môn Tin Học THPT

Trình độ: Cử nhân Sư phạm Tin học, Chứng chỉ hạng II, Chứng chỉ Tin học, Ngoại ngữ B1

Kinh nghiệm: 8+ năm kinh nghiệm tại Trường THPT Thuận Hóa