print()
trong Python: Tham số mặc định và kết quả bất ngờBạn có bao giờ tự hỏi tại sao một hàm trong Python lại cho ra những kết quả khác nhau khi được gọi nhiều lần với những đối số thoạt nhìn có vẻ giống nhau? Bài viết này sẽ đi sâu vào một khía cạnh thú vị của Python, đó là cách hàm print()
hoạt động với các tham số mặc định, đặc biệt là khi chúng là các đối tượng có thể thay đổi (mutable objects) như list. Hiểu rõ điều này sẽ giúp bạn tránh được những lỗi khó hiểu và viết code Python hiệu quả hơn.
Hãy xem xét đoạn code sau:
def f(x, arr=[]):
arr.append(x)
return arr
print(f(1), f(2)) # Output: [1, 2] [1, 2]
print(f(1)) # Output: [1]
print(f(2)) # Output: [1, 2]
Tại sao khi gọi print(f(1), f(2))
, chúng ta lại nhận được [1, 2] [1, 2]
, trong khi khi gọi riêng lẻ print(f(1))
rồi print(f(2))
, kết quả lại là [1]
và [1, 2]
? Đây là một ví dụ điển hình về cách các tham số mặc định trong Python có thể gây ra những hành vi không mong muốn.
Bí mật nằm ở hai yếu tố chính:
arr=[]
chỉ được tạo ra một lần khi hàm f
được định nghĩa. Các lần gọi hàm sau đó sẽ sử dụng lại chính list này, thay vì tạo một list mới.
append()
sẽ thêm phần tử vào list hiện có.
Khi gọi print(f(1), f(2))
, Python sẽ thực hiện các bước sau:
f(1)
: 1
được thêm vào list arr
(list này được tạo ra khi định nghĩa hàm f
), arr
trở thành [1]
. Hàm f(1)
trả về tham chiếu đến list arr
.
f(2)
: 2
được thêm vào list arr
(vẫn là list cũ), arr
trở thành [1, 2]
. Hàm f(2)
trả về tham chiếu đến list arr
.
print()
nhận hai tham số, đều là tham chiếu đến cùng một list arr
(có giá trị [1, 2]
). Do đó, nó in ra [1, 2] [1, 2]
.
Trong khi đó, khi gọi print(f(1))
rồi print(f(2))
:
f(1)
: 1
được thêm vào list arr
, arr
trở thành [1]
. Hàm print()
in ra [1]
.
f(2)
: 2
được thêm vào list arr
(vẫn là list cũ), arr
trở thành [1, 2]
. Hàm print()
in ra [1, 2]
.
Cách tốt nhất để tránh những rắc rối này là không sử dụng các đối tượng có thể thay đổi như list, dictionary, set làm tham số mặc định. Thay vào đó, bạn có thể sử dụng None
và tạo một đối tượng mới bên trong hàm nếu tham số đó không được truyền vào:
def f(x, arr=None):
if arr is None:
arr = []
arr.append(x)
return arr
print(f(1), f(2)) # Output: [1] [2]
print(f(1)) # Output: [1]
print(f(2)) # Output: [2]
Với cách này, mỗi lần gọi hàm f
mà không truyền tham số arr
, một list mới sẽ được tạo ra, đảm bảo kết quả luôn nhất quán.
Hiểu rõ cách Python xử lý các tham số mặc định, đặc biệt là khi chúng là các đối tượng có thể thay đổi, là vô cùng quan trọng để viết code rõ ràng, dễ bảo trì và tránh được những lỗi tiềm ẩn. Hãy luôn cẩn trọng khi sử dụng tham số mặc định và cân nhắc sử dụng None
làm giá trị mặc định nếu bạn muốn tạo một đối tượng mới mỗi khi hàm được gọi mà không có tham số đó.
Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về một khía cạnh thú vị của Python. Chúc bạn thành công trên con đường chinh phục ngôn ngữ lập trình mạnh mẽ này!
Bài viết liên quan