Closure trong Python

Table of Content

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ố, arg1arg2:

>>> 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ạy
  • arg1: đối số đầu tiên của func
  • arg2: đối số thứ hai của func
>>> 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àm knights2 thay vì dùng như đối số.
  • knights() trả về hàm inner2 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 ab 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

Leave a Reply