Scope và Namespace
Scope (phạm vi) và Namespace (không gian tên) là hai khái niệm quan trọng giúp Python quản lý biến và tránh xung đột tên trong chương trình.
1. Namespace là gì?
Phần tiêu đề “1. Namespace là gì?”Định nghĩa
Phần tiêu đề “Định nghĩa”Namespace là một “không gian tên” - một tập hợp các tên (identifiers) và các đối tượng mà chúng tham chiếu đến. Mỗi tên trong namespace phải là duy nhất.
Hãy tưởng tượng namespace như một cuốn từ điển:
- Key (tên biến):
age,name,calculate - Value (giá trị):
25,"John",<function>
# Namespace giống như một dictionary# Ví dụ minh họa (không phải code thực)# namespace = {# 'x': 10,# 'name': 'Python',# 'calculate': <function object># }
x = 10name = "Python"def calculate(): passCác loại Namespace
Phần tiêu đề “Các loại Namespace”Python có 4 loại namespace chính:
1. Built-in Namespace (Namespace tích hợp)
Phần tiêu đề “1. Built-in Namespace (Namespace tích hợp)”Chứa các tên của các hàm và biến được tích hợp sẵn trong Python (như print(), len(), int(), v.v.).
# Các hàm built-in luôn có sẵnprint(len([1, 2, 3])) # len là built-inprint(type(5)) # type là built-inprint(abs(-10)) # abs là built-in
# Xem tất cả built-in namesimport builtinsprint(dir(builtins))2. Global Namespace (Namespace toàn cục)
Phần tiêu đề “2. Global Namespace (Namespace toàn cục)”Chứa các tên được định nghĩa ở cấp độ module (file Python).
# Đây là global namespace của file nàyglobal_var = "Tôi là biến toàn cục"PI = 3.14159
def my_function(): pass
class MyClass: pass
# Biến global_var, PI, my_function, MyClass đều nằm trong global namespace3. Enclosing Namespace (Namespace bao bọc)
Phần tiêu đề “3. Enclosing Namespace (Namespace bao bọc)”Chứa các tên trong hàm bên ngoài (outer function) khi có nested functions.
def outer_function(): outer_var = "Tôi là biến của outer function"
def inner_function(): # inner_function có thể truy cập outer_var # outer_var nằm trong enclosing namespace print(outer_var)
inner_function()
outer_function() # In: Tôi là biến của outer function4. Local Namespace (Namespace cục bộ)
Phần tiêu đề “4. Local Namespace (Namespace cục bộ)”Chứa các tên được định nghĩa bên trong một hàm.
def my_function(): local_var = "Tôi là biến cục bộ" x = 10 # local_var và x chỉ tồn tại trong hàm này print(local_var)
my_function()# print(local_var) # Lỗi: local_var không tồn tại ngoài hàm2. Scope là gì?
Phần tiêu đề “2. Scope là gì?”Định nghĩa
Phần tiêu đề “Định nghĩa”Scope (phạm vi) là khu vực trong code nơi một namespace có thể được truy cập trực tiếp. Scope xác định “phạm vi hoạt động” của một biến.
LEGB Rule - Quy tắc tìm kiếm biến
Phần tiêu đề “LEGB Rule - Quy tắc tìm kiếm biến”Python tìm kiếm biến theo thứ tự LEGB:
- L - Local: Scope cục bộ (trong hàm hiện tại)
- E - Enclosing: Scope bao bọc (hàm bên ngoài)
- G - Global: Scope toàn cục (cấp độ module)
- B - Built-in: Scope tích hợp sẵn
x = "global" # Global scope
def outer(): x = "enclosing" # Enclosing scope
def inner(): x = "local" # Local scope print(x)
inner()
outer() # In: local
# Python tìm x theo thứ tự: Local → Enclosing → Global → Built-inMinh họa LEGB Rule
Phần tiêu đề “Minh họa LEGB Rule”# Built-in: len() có sẵn trong Python# Global: biến x ở cấp modulex = "global x"
def outer(): # Enclosing: biến x trong outer function x = "enclosing x"
def inner(): # Local: biến x trong inner function x = "local x" print(f"1. Local x: {x}") # Tìm thấy ở Local
def inner2(): # Không có x local, tìm ở Enclosing print(f"2. Enclosing x: {x}")
inner() inner2()
outer()print(f"3. Global x: {x}") # Tìm thấy ở Global
# Kết quả:# 1. Local x: local x# 2. Enclosing x: enclosing x# 3. Global x: global x3. Local Scope (Phạm vi cục bộ)
Phần tiêu đề “3. Local Scope (Phạm vi cục bộ)”Biến local
Phần tiêu đề “Biến local”Biến được tạo bên trong hàm chỉ tồn tại trong hàm đó.
def my_function(): local_var = 100 # Biến local print(f"Trong hàm: {local_var}")
my_function() # In: Trong hàm: 100
# print(local_var) # Lỗi: name 'local_var' is not definedTham số của hàm cũng là biến local
Phần tiêu đề “Tham số của hàm cũng là biến local”def greet(name): # 'name' là biến local message = f"Hello, {name}!" # 'message' cũng là local print(message)
greet("Alice") # In: Hello, Alice!
# print(name) # Lỗi: không tồn tại ngoài hàm# print(message) # Lỗi: không tồn tại ngoài hàmBiến local không ảnh hưởng biến global cùng tên
Phần tiêu đề “Biến local không ảnh hưởng biến global cùng tên”x = "global"
def my_function(): x = "local" # Tạo biến local mới, không ảnh hưởng global x print(f"Trong hàm: {x}")
my_function() # In: Trong hàm: localprint(f"Ngoài hàm: {x}") # In: Ngoài hàm: global4. Global Scope (Phạm vi toàn cục)
Phần tiêu đề “4. Global Scope (Phạm vi toàn cục)”Biến global
Phần tiêu đề “Biến global”Biến được tạo ở cấp độ module (ngoài tất cả các hàm).
# Biến globalglobal_var = "Tôi là biến toàn cục"
def function1(): print(global_var) # Đọc biến global: OK
def function2(): print(global_var) # Cũng đọc được
function1() # In: Tôi là biến toàn cụcfunction2() # In: Tôi là biến toàn cụcprint(global_var) # In: Tôi là biến toàn cụcTừ khóa global
Phần tiêu đề “Từ khóa global”Để thay đổi biến global bên trong hàm, phải dùng từ khóa global.
count = 0 # Biến global
def increment(): global count # Khai báo sẽ dùng biến global count += 1 # Thay đổi biến global print(f"Count = {count}")
increment() # Count = 1increment() # Count = 2increment() # Count = 3print(count) # 3Lỗi khi không dùng global
Phần tiêu đề “Lỗi khi không dùng global”counter = 0
def increment(): # Không có từ khóa global # counter += 1 # Lỗi: UnboundLocalError # Python nghĩ counter là biến local nhưng chưa được gán giá trị pass
# Cách đúng: dùng globaldef increment_correct(): global counter counter += 1
increment_correct()print(counter) # 1Khi nào nên dùng global?
Phần tiêu đề “Khi nào nên dùng global?”# ❌ KHÔNG NÊN: Lạm dụng globalscore = 0
def add_score(points): global score score += points
def reset_score(): global score score = 0
# ✅ NÊN: Dùng tham số và returndef add_score(current_score, points): return current_score + points
score = 0score = add_score(score, 10)score = add_score(score, 5)print(score) # 155. Enclosing Scope (Phạm vi bao bọc)
Phần tiêu đề “5. Enclosing Scope (Phạm vi bao bọc)”Nested Functions
Phần tiêu đề “Nested Functions”Hàm bên trong có thể truy cập biến của hàm bên ngoài.
def outer(): x = "outer x"
def inner(): print(x) # Truy cập biến của outer
inner()
outer() # In: outer xTừ khóa nonlocal
Phần tiêu đề “Từ khóa nonlocal”Để thay đổi biến của hàm bên ngoài, dùng từ khóa nonlocal.
def outer(): count = 0
def inner(): nonlocal count # Khai báo dùng biến của outer count += 1 print(f"Count = {count}")
inner() # Count = 1 inner() # Count = 2 print(f"Final count = {count}") # Final count = 2
outer()Lỗi khi không dùng nonlocal
Phần tiêu đề “Lỗi khi không dùng nonlocal”def outer(): x = 10
def inner(): # x += 1 # Lỗi: UnboundLocalError # Python nghĩ x là local nhưng chưa được gán pass
inner()
# Cách đúng:def outer_correct(): x = 10
def inner(): nonlocal x x += 1 print(x)
inner() # In: 11
outer_correct()Ví dụ thực tế: Closure
Phần tiêu đề “Ví dụ thực tế: Closure”def make_multiplier(n): def multiplier(x): return x * n # Truy cập n từ enclosing scope return multiplier
# Tạo các hàm nhântimes_2 = make_multiplier(2)times_3 = make_multiplier(3)times_5 = make_multiplier(5)
print(times_2(10)) # 20print(times_3(10)) # 30print(times_5(10)) # 506. Built-in Scope
Phần tiêu đề “6. Built-in Scope”Namespace tích hợp sẵn
Phần tiêu đề “Namespace tích hợp sẵn”Python cung cấp sẵn nhiều hàm và biến.
# Các hàm built-inprint()len()type()int()str()list()dict()
# Xem tất cả built-inimport builtinsprint(dir(builtins))Ghi đè built-in (KHÔNG NÊN làm)
Phần tiêu đề “Ghi đè built-in (KHÔNG NÊN làm)”# ❌ KHÔNG NÊN: Ghi đè built-in functionprint("Before override")
print = "Now print is a string"# print("After override") # Lỗi: 'str' object is not callable
# Khôi phục (phức tạp)import builtinsprint = builtins.printprint("Restored") # Hoạt động lại
# ❌ KHÔNG NÊN đặt tên biến trùng built-inlist = [1, 2, 3] # Ghi đè hàm list()# my_list = list(range(5)) # Lỗi: 'list' object is not callable7. Hàm globals() và locals()
Phần tiêu đề “7. Hàm globals() và locals()”globals() - Xem global namespace
Phần tiêu đề “globals() - Xem global namespace”x = 10y = 20
def my_function(): print(globals()) # Dictionary chứa tất cả biến global
my_function()
# Truy cập biến global qua globals()print(globals()['x']) # 10
# Thêm biến global độngglobals()['z'] = 30print(z) # 30locals() - Xem local namespace
Phần tiêu đề “locals() - Xem local namespace”def my_function(): a = 1 b = 2 c = 3 print(locals()) # {'a': 1, 'b': 2, 'c': 3}
my_function()
# Ở global scope, locals() == globals()x = 100print(locals() == globals()) # True8. Ví dụ thực tế
Phần tiêu đề “8. Ví dụ thực tế”Counter với closure
Phần tiêu đề “Counter với closure”def create_counter(): count = 0
def increment(): nonlocal count count += 1 return count
def decrement(): nonlocal count count -= 1 return count
def get_count(): return count
return increment, decrement, get_count
# Tạo counterinc, dec, get = create_counter()
print(inc()) # 1print(inc()) # 2print(inc()) # 3print(dec()) # 2print(get()) # 2Configuration Manager
Phần tiêu đề “Configuration Manager”APP_NAME = "My Application"VERSION = "1.0.0"DEBUG = True
def get_config(): return { 'app_name': APP_NAME, 'version': VERSION, 'debug': DEBUG }
def set_debug(value): global DEBUG DEBUG = value
# main.pyprint(get_config())set_debug(False)print(get_config())Bank Account với encapsulation
Phần tiêu đề “Bank Account với encapsulation”def create_account(initial_balance): balance = initial_balance # Private variable (enclosing scope)
def deposit(amount): nonlocal balance if amount > 0: balance += amount return f"Deposited {amount}. New balance: {balance}" return "Invalid amount"
def withdraw(amount): nonlocal balance if 0 < amount <= balance: balance -= amount return f"Withdrew {amount}. New balance: {balance}" return "Insufficient funds or invalid amount"
def get_balance(): return balance
return deposit, withdraw, get_balance
# Tạo tài khoảndeposit, withdraw, get_balance = create_account(1000)
print(get_balance()) # 1000print(deposit(500)) # Deposited 500. New balance: 1500print(withdraw(200)) # Withdrew 200. New balance: 1300print(get_balance()) # 1300
# Không thể truy cập trực tiếp biến balance từ bên ngoài9. Bài tập thực hành
Phần tiêu đề “9. Bài tập thực hành”Bài 1: Phân biệt scope
Phần tiêu đề “Bài 1: Phân biệt scope”x = "global"
def outer(): x = "outer"
def inner(): x = "inner" print(f"inner x = {x}")
inner() print(f"outer x = {x}")
outer()print(f"global x = {x}")
# Kết quả:# inner x = inner# outer x = outer# global x = globalBài 2: Tạo ID generator
Phần tiêu đề “Bài 2: Tạo ID generator”def create_id_generator(start=1): current_id = start - 1
def next_id(): nonlocal current_id current_id += 1 return current_id
def reset(): nonlocal current_id current_id = start - 1
return next_id, reset
# Sử dụngnext_id, reset = create_id_generator(100)
print(next_id()) # 100print(next_id()) # 101print(next_id()) # 102reset()print(next_id()) # 100Bài 3: Temperature Converter
Phần tiêu đề “Bài 3: Temperature Converter”CELSIUS_TO_FAHRENHEIT = lambda c: (c * 9/5) + 32FAHRENHEIT_TO_CELSIUS = lambda f: (f - 32) * 5/9
def temperature_converter(): last_conversion = None
def celsius_to_fahrenheit(celsius): nonlocal last_conversion fahrenheit = CELSIUS_TO_FAHRENHEIT(celsius) last_conversion = f"{celsius}°C = {fahrenheit}°F" return fahrenheit
def fahrenheit_to_celsius(fahrenheit): nonlocal last_conversion celsius = FAHRENHEIT_TO_CELSIUS(fahrenheit) last_conversion = f"{fahrenheit}°F = {celsius}°C" return celsius
def get_last_conversion(): return last_conversion
return celsius_to_fahrenheit, fahrenheit_to_celsius, get_last_conversion
# Sử dụngc_to_f, f_to_c, get_last = temperature_converter()
print(c_to_f(25)) # 77.0print(get_last()) # 25°C = 77.0°Fprint(f_to_c(98.6)) # 37.0print(get_last()) # 98.6°F = 37.0°CTổng kết
Phần tiêu đề “Tổng kết”-
Namespace: Không gian chứa các tên (biến, hàm, class) và giá trị của chúng
- Built-in, Global, Enclosing, Local
-
Scope: Phạm vi mà một namespace có thể được truy cập
- LEGB Rule: Local → Enclosing → Global → Built-in
-
Từ khóa quan trọng:
global: Thay đổi biến global từ trong hàmnonlocal: Thay đổi biến của hàm bên ngoài (enclosing)
-
Best Practices:
- ✅ Hạn chế dùng biến global
- ✅ Dùng tham số và return thay vì global
- ✅ Không ghi đè built-in functions/variables
- ✅ Sử dụng closure để encapsulation dữ liệu