2.15. Đối số¶
Một hàm có thể được gọi theo nhiều cách, và các tham số của nó có thể được khai báo theo nhiều dạng khác nhau. Các tổ hợp này trông có vẻ đáng sợ lúc đầu; nhưng trên thực tế ba hoặc bốn mẫu là đủ để bao phủ hầu hết mọi trường hợp.
2.15.1. Đối số theo vị trí và đối số từ khóa¶
Cách gọi đơn giản nhất truyền đối số theo vị trí -- giá trị đầu tiên vào tham số đầu tiên, giá trị thứ hai vào tham số thứ hai, và cứ thế tiếp tục:
def rect(x, y, w, h):
return (x, y, w, h)
rect(10, 20, 100, 50)
Cùng một lời gọi có thể truyền đối số theo từ khóa, đặt tên từng tham số một cách rõ ràng:
rect(x=10, y=20, w=100, h=50)
Đối số từ khóa không phụ thuộc vào thứ tự và giúp lời gọi tự giải thích, nhưng phải gõ nhiều hơn. Đối số vị trí và đối số từ khóa có thể kết hợp trong một lời gọi, nhưng mọi đối số vị trí phải đứng trước bất kỳ đối số từ khóa nào:
rect(10, 20, w=100, h=50) # OK
rect(x=10, 20, 100, 50) # SyntaxError
2.15.2. Giá trị mặc định¶
Một tham số có thể khai báo giá trị mặc định để sử dụng khi người gọi không cung cấp giá trị:
def greet(name, greeting="hello"):
print(greeting, name)
greet("Alice") # hello Alice
greet("Alice", "hi") # hi Alice
greet("Alice", greeting="hey") # hey Alice
Các tham số có giá trị mặc định phải đứng sau các tham số không có giá trị mặc định trong dòng def.
Cảnh báo
Giá trị mặc định được đánh giá một lần, khi def chạy -- không phải mỗi lần gọi. Sử dụng giá trị mặc định có thể thay đổi ([], {}) khiến cùng một đối tượng được chia sẻ qua mỗi lần gọi lấy giá trị mặc định. Dùng None làm giá trị sentinel thay thế:
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return target
2.15.3. Độ dài biến đổi: *args và **kwargs¶
Một tham số có tiền tố * thu thập tất cả đối số vị trí còn lại vào một tuple. Một tham số có tiền tố ** thu thập tất cả đối số từ khóa còn lại vào một dict. Tên thông thường là args và kwargs, nhưng bất kỳ tên định danh nào cũng dùng được:
def report(label, *values, **options):
print(label, values, options)
report("temps", 21, 22, 23, unit="C", precision=1)
Kết quả:
temps (21, 22, 23) {'unit': 'C', 'precision': 1}
Một hàm hiếm khi cần cả hai. Cách dùng phổ biến nhất là chuyển tiếp đối số từ hàm bao đến lời gọi bên trong:
def log_and_call(func, *args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
Cú pháp đối xứng ở phía lời gọi giải nén một iterable thành đối số vị trí (*) hoặc một dict thành đối số từ khóa (**):
point = (10, 20, 100, 50)
rect(*point) # same as rect(10, 20, 100, 50)
kwargs = {"x": 10, "y": 20, "w": 100, "h": 50}
rect(**kwargs) # same as rect(x=10, y=20, ...)
2.15.4. Tham số chỉ từ khóa¶
Một * đơn lẻ trong danh sách tham số (không gắn với tên) đánh dấu mọi tham số theo sau là chỉ từ khóa -- người gọi phải dùng tên:
def crop(buffer, *, x, y, w, h):
...
crop(buffer, x=0, y=0, w=100, h=100) # OK
crop(buffer, 0, 0, 100, 100) # TypeError
Dùng cách này cho các cờ boolean và các đối số khác mà khi chỉ có giá trị tại lời gọi sẽ không tự giải thích được ý nghĩa.