DHDataHọc
Lý thuyếtBài 20/304 phút đọc+10 XP

Tham số và đối số

Bạn sẽ học được

  • Phân biệt tham số (parameter) và đối số (argument).
  • Truyền theo vị trí, theo tên, và trộn hai cách.
  • Giá trị mặc định — cùng cái bẫy mutable default.
  • *args, **kwargs và quy tắc thứ tự.
  • Tham số chỉ theo từ khoá và chỉ theo vị trí.

Khái niệm cốt lõi

Tham số là tên trong định nghĩa hàm; đối số là giá trị truyền khi gọi:

code
def area(width, height):
    return width * height

print(area(3, 2))
code
6

width, height là tham số; 3, 2 là đối số. Truyền theo vị trí: 3 vào width, 2 vào height. Có thể truyền theo tên:

code
print(area(width=3, height=2))
print(area(height=2, width=3))
code
6
6

Truyền theo tên cho phép đổi thứ tự và làm lời gọi tự giải thích. Quy tắc: đối số vị trí phải đứng trước đối số từ khoá. area(width=3, 2) là SyntaxError.

Giá trị mặc định

code
def area(width, height=1.0):
    return width * height

print(area(3), area(3, 2))
code
3.0 6

Tham số có mặc định phải nằm sau tham số không mặc định. Nếu viết def area(width=1.0, height) sẽ báo SyntaxError: non-default argument follows default argument.

Mặc định được đánh giá một lần khi def chạy, không phải mỗi lần gọi. Với giá trị bất biến như số, chuỗi, None, điều này vô hại. Với list/dict thì không:

code
def add_item(item, bucket=[]):
    bucket.append(item)
    return bucket

print(add_item("a"))
print(add_item("b"))
code
['a']
['a', 'b']

Lần gọi thứ hai dùng lại cùng một list đã bị sửa ở lần đầu. Mặc định không được "làm mới". Cách sửa chuẩn:

code
def add_item_ok(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

print(add_item_ok("a"))
print(add_item_ok("b"))
code
['a']
['b']

Dùng None làm mặc định và tạo list bên trong thân hàm. Tương tự với {}, set(), và mọi đối tượng thay đổi được. Đây là một trong những lỗi Python nổi tiếng nhất, xuất hiện cả trong code production.

*args — nhiều đối số vị trí

code
def total(*nums):
    return sum(nums)

print(total(1, 2, 3))
print(total())
code
6
0

Bên trong hàm, nums là tuple chứa mọi đối số vị trí thừa. Có thể truyền sẵn một list bằng cách mở gói:

code
values = [1, 2, 3]
print(total(*values))
code
6

Tên args chỉ là quy ước; *nums cũng đúng. Điều quan trọng là dấu *.

**kwargs — nhiều đối số từ khoá

code
def show(**info):
    for key, value in info.items():
        print(key, value)

show(name="An", age=28)
code
name An
age 28

info là dict chứa các đối số từ khoá không khớp tham số nào khác. Mở gói dict khi gọi:

code
person = {"name": "An", "age": 28}
show(**person)

Lưu ý: tên khóa phải là chuỗi hợp lệ làm tên tham số; **{"first-name": "An"} sẽ lỗi khi gọi hàm nhận **kwargs? Thực tế Python cho phép khóa bất kỳ trong **kwargs khi gọi, nhưng nếu hàm có tham số cụ thể thì chỉ khớp tên hợp lệ. Với **info thuần, khóa lạ vẫn vào dict — hãy kiểm tra dữ liệu thay vì tin tưởng.

Thứ tự tham số

Khi kết hợp, thứ tự bắt buộc:

code
def ham(a, b=1, *args, c=2, **kwargs):
    ...
Vị tríLoại
1Tham số thường
2Tham số có mặc định
3*args
4Tham số chỉ theo từ khoá
5**kwargs

Sai thứ tự là SyntaxError ngay khi định nghĩa, nên lỗi lộ ra sớm.

Tham số chỉ theo từ khoá

Dấu * trần đánh dấu mọi tham số sau nó phải truyền theo tên:

code
def area(width, height, *, unit="cm"):
    return f"{width * height} {unit}²"

print(area(3, 2))
print(area(3, 2, unit="m"))
code
6 cm²
6 m²

area(3, 2, "m") sẽ là TypeError: area() takes 2 positional arguments but 3 were given. Tham số chỉ theo từ khoá rất hữu ích khi thêm tuỳ chọn mới vào hàm đã có người dùng — tránh việc đối số vị trí cũ bị hiểu nhầm.

Ngược lại, dấu / đánh dấu tham số chỉ theo vị trí (Python 3.8+):

code
def chia(a, b, /):
    return a / b

print(chia(10, 2))
code
5.0

chia(a=10, b=2) báo TypeError. Ít dùng trong code ứng dụng; chủ yếu trong thư viện chuẩn.

Thực hành

  1. Gọi area(3, 2) theo vị trí và theo tên, đổi thứ tự.
  2. Thêm mặc định height=1.0 và thử bỏ đối số thứ hai.
  3. Tái hiện bẫy bucket=[], rồi sửa bằng None.
  4. Viết total(*nums) và gọi với 0, 3, 5 đối số.
  5. Viết show(**info) và gọi bằng **dict.
  6. Đổi unit thành tham số chỉ theo từ khoá và thử truyền vị trí để đọc lỗi.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
SyntaxError: non-default argument follows defaultMặc định đứng trước tham số thườngĐưa mặc định ra sau
TypeError: missing required argumentThiếu đối sốTruyền đủ hoặc thêm mặc định
TypeError: got multiple valuesTruyền cùng tham số hai lầnKiểm tra vị trí và tên
List mặc định giữ dữ liệu cũMutable defaultDùng None rồi tạo mới
TypeError: takes N positional argumentsTruyền vị trí vào tham số keyword-onlyTruyền theo tên
*args là tuple, không phải listBản chất của *argslist(args) nếu cần sửa
**kwargs nhận khóa lạKhông kiểm tra dữ liệuLọc khóa hợp lệ trước

Tóm tắt

  • Tham số trong def, đối số khi gọi; vị trí trước, từ khoá sau.
  • Mặc định đánh giá một lần — không dùng list/dict làm mặc định.
  • *args gom tuple, **kwargs gom dict; mở gói bằng */** khi gọi.
  • * trần tạo tham số chỉ theo từ khoá; / tạo chỉ theo vị trí.
  • Thứ tự tham số sai báo lỗi ngay khi định nghĩa.

Dùng ← → để chuyển bài