Closure
1. Tham chiếu hàm
def test1():
print("--- Trong hàm test1 ---")
# Gọi hàm
test1()
# Tham chiếu hàm
ret = test1
print(id(ret))
print(id(test1))
# Gọi hàm thông qua tham chiếu
ret()
Kết quả chạy:
--- Trong hàm test1 ---
140212571149040
140212571149040
--- Trong hàm test1 ---
2. Định nghĩa Closure
# Định nghĩa một hàm
def test(number):
# Hàm bên trong sử dụng biến từ hàm bên ngoài, tạo thành closure
def test_in(number_in):
print("Trong hàm test_in, number_in là %d" % number_in)
return number + number_in
# Trả về kết quả của closure
return test_in
# Gán giá trị 20 cho tham số number
ret = test(20)
# Gọi hàm với tham số 100
print(ret(100))
# Gọi hàm với tham số 200
print(ret(200))
Kết quả chạy:
Trong hàm test_in, number_in là 100
120
Trong hàm test_in, number_in là 200
220
3. Ví dụ thực tế về closure
def line_conf(a, b):
def line(x):
return a * x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
Trong ví dụ này, hàm `line` và các biến `a`, `b` tạo thành closure. Khi tạo closure, chúng ta xác định giá trị của `a` và `b` thông qua tham số của `line_conf`. Điều này giúp chúng ta có thể tạo ra các hàm đường thẳng khác nhau chỉ bằng cách thay đổi `a` và `b`.
Nếu không có closure, chúng ta cần phải truyền nhiều tham số hơn mỗi khi tạo hàm mới, làm giảm tính di động của mã.
Lưu ý: Do closure tham chiếu đến biến cục bộ của hàm bên ngoài, nên các biến này không được giải phóng ngay lập tức, dẫn đến tiêu tốn bộ nhớ.
4. Sửa đổi biến trong hàm bên ngoài
Phương pháp Python 3
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
Phương pháp Python 2
def counter(start=0):
count = [start]
def incr():
count[0] += 1
return count[0]
return incr
c1 = counter(5)
print(c1()) # 6
print(c1()) # 7
c2 = counter(100)
print(c2()) # 101
print(c2()) # 102
Decorator
1. Hiểu rõ đoạn mã sau
#### Vòng 1 ####
def foo():
print('foo')
foo # Đang biểu thị cho hàm
foo() # Gọi hàm foo
#### Vòng 2 ####
def foo():
print('foo')
foo = lambda x: x + 1
foo() # Gọi hàm lambda, không còn là hàm foo ban đầu
Tên hàm chỉ là một biến, trỏ đến hàm đã định nghĩa. Nếu tên hàm bị gán lại, thì gọi hàm sẽ thực hiện hàm mới.
2. Yêu cầu
Công ty có nhiều bộ phận kinh doanh, và bộ phận nền tảng cung cấp các chức năng cơ bản. Các bộ phận kinh doanh sử dụng các chức năng này. Ví dụ:
############### Chức năng nền tảng ###############
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
def f4():
print('f4')
############### Bộ phận A sử dụng chức năng nền tảng ###############
f1()
f2()
f3()
f4()
############### Bộ phận B sử dụng chức năng nền tảng ###############
f1()
f2()
f3()
f4()
Hiện tại, cần thêm cơ chế xác thực vào tất cả các chức năng nền tảng.
3. Sử dụng decorator
def w1(func):
def inner():
# Xác thực
func()
return inner
@w1
def f1():
print('f1')
@w1
def f2():
print('f2')
@w1
def f3():
print('f3')
@w1
def f4():
print('f4')
Decorator `w1` sẽ bọc hàm `f1`, `f2`, `f3`, `f4` và thực hiện xác thực trước khi gọi hàm gốc.
4. Ví dụ về decorator
from time import ctime, sleep
def timefun(func):
def wrapped_func():
print("%s called at %s" % (func.__name__, ctime()))
func()
return wrapped_func
@timefun
def foo():
print("I am foo")
foo()
sleep(2)
foo()
Kết quả chạy:
foo called at Fri Nov 4 21:55:35 2016
I am foo
foo called at Fri Nov 4 21:55:37 2016
I am foo
5. Decorator với tham số
def timefun_arg(pre="hello"):
def timefun(func):
def wrapped_func():
print("%s called at %s %s" % (func.__name__, ctime(), pre))
return func()
return wrapped_func
return timefun
@timefun_arg("itcast")
def foo():
print("I am foo")
@timefun_arg("python")
def too():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
6. Decorator lớp
class Test(object):
def __init__(self, func):
self.__func = func
def __call__(self):
print("--- Chức năng decorator ---")
self.__func()
@Test
def test():
print("----test---")
test()
Kết quả chạy:
--- Chức năng decorator ---
----test---