Hàm cũng là một đối tượng
Trong Python, mọi thứ đều là đối tượng. Đối tượng bao gồm các số, các chuỗi, các tuple, các danh sách, các từ điển – và cũng gồm các hàm. Bạn có thể gán hàm cho một biến, truyền hàm vào một hàm khác và trả về hàm từ một hàm khác.
Để kiểm tra, cùng định nghĩa hàm answer()
không có tham số, chỉ in ra số 42
:
>>> def answer():
print(42)
Nếu bạn chạy hàm này, bạn sẽ nhận được:
>>> answer()
42
Giờ cùng định nghĩa một hàm khác tên là run_something
. Nó có một tham số func
là hàm để chạy. Trong run_something
ta chỉ đơn giản gọi func()
.
>>> def run_something(func):
func()
Khi gọi run_something
ta truyền hàm answer
vào để chạy hàm này:
>>> run_something(answer)
42
Các hàm được xem là đối tượng có kiểu function
:
>>> type(run_something)
<class 'function'>
Cùng thử một hàm khác có tham số. Định nghĩa hàm add_args()
in tổng của hai số, arg1
và arg2
:
>>> def add_args(arg1, arg2):
print(arg1 + arg2)
Tiếp theo định nghĩa hàm run_something_with_args()
có ba tham số:
func
: hàm để chạyarg1
: đối số đầu tiên củafunc
arg2
: đối số thứ hai củafunc
>>> def run_something_with_args(func, arg1, arg2):
func(arg1, arg2)
>>> run_something_with_args(add_args, 5, 9)
14
Hàm nội
Bạn có thể định nghĩa một hàm bên trong một hàm khác:
>>> def outer(a, b):
def inner(c, d):
return c + d
return inner(a, b)
>>> outer(4,7)
11
Hàm nội hữu ích khi cần thực hiện một hành động phức tạp nhiều hơn một lần trong một hàm. Ví dụ, hàm nội trong hàm sau thêm văn bản tới đối số của nó:
>>> def knights(saying):
def inner(quote):
return "We are the knights who say: '%s'" % quote
return inner(saying)
>>> knights('Ni!')
"We are the knights who say: 'Ni!'"
Closure
Một hàm nội có thể đóng vai trò một closure. Closure là một hàm được sinh ra từ một hàm khác, có thể thay đổi và ghi nhớ giá trị của các biến được khai báo trong hàm sinh ra nó.
Ví dụ sau được xây dựng dựa trên hàm knights
ở trên.
>>> def knights2(saying):
def inner2():
return "We are the knights who say: '%s'" % saying
return inner2
Có những điểm khác biệt sau:
inner2()
sử dụng trực tiếp tham sốsaying
của hàmknights2
thay vì dùng như đối số.knights()
trả về hàminner2
chứ không gọi nó.
Hàm inner2
biết giá trị saying
và ghi nhớ nó. Dòng return inner2
trả về tham chiếu tới inner2
chứ không gọi nó.
Cùng gọi knights2()
hai lần, với hai đối số khác nhau:
>>> a = knights2('Duck')
>>> b = knights2('Hasenpfeffer')
Kiểu của a
và b
là gì?
>>> type(a)
<class 'function'>
>>> type(b)
<class 'function'>
Chúng là hàm, nhưng cũng là closure.
>>> a
<function knights2.<locals>.inner2 at 0x10193e158>
>>> b
<function knights2.<locals>.inner2 at 0x10193e1e0>
Nếu chúng ta gọi chúng, chúng nhớ saying
chúng đã sử dụng khi chúng được tạo bởi knights2
:
>>> a()
"We are the knights who say: 'Duck'"
>>> b()
"We are the knights who say: 'Hasenpfeffer'"
Đây là một ví dụ khác:
def outer_func(x):
y = 4
def inner_func(z):
print(f"x = {x}, y = {y}, z = {z}")
return x + y + z
return inner_func
for i in range(3):
closure = outer_func(i)
print(f"closure({i+5}) = {closure(i+5)}")
Kết quả khi chạy:
x = 0, y = 4, z = 5
closure(5) = 9
x = 1, y = 4, z = 6
closure(6) = 11
x = 2, y = 4, z = 7
closure(7) = 13