Bạn thực sự không muốn đi xuống hố thỏ này, nhưng nếu bạn nhấn mạnh, điều đó là có thể. Với một số công việc.
Chức năng lồng nhau được tạo ra lần nữa cho mỗi cuộc gọi đến make_adder()
:
>>> import dis
>>> dis.dis(make_adder)
2 0 LOAD_CLOSURE 0 (x)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object adder at 0x10fc988b0, file "<stdin>", line 2>)
9 MAKE_CLOSURE 0
12 STORE_FAST 1 (adder)
4 15 LOAD_FAST 1 (adder)
18 RETURN_VALUE
Các MAKE_CLOSURE
opcode có tạo một hàm với một kết thúc, một hàm lồng nhau đề cập đến x
từ hàm mẹ (LOAD_CLOSURE
opcode xây dựng các tế bào đóng cửa cho chức năng).
Nếu không gọi hàm make_adder
, bạn chỉ có thể truy cập đối tượng mã; nó được lưu trữ như một hằng số với mã chức năng make_adder()
. Mã byte cho adder
đếm vào khả năng truy cập vào biến x
như một tế bào scoped, tuy nhiên, mà làm cho các đối tượng đang gần như vô ích cho bạn:
>>> make_adder.__code__.co_consts
(None, <code object adder at 0x10fc988b0, file "<stdin>", line 2>)
>>> dis.dis(make_adder.__code__.co_consts[1])
3 0 LOAD_DEREF 0 (x)
3 LOAD_FAST 0 (y)
6 BINARY_ADD
7 RETURN_VALUE
LOAD_DEREF
tải một giá trị từ một tế bào đóng cửa. Để thực hiện các đối tượng mã vào một đối tượng chức năng một lần nữa, bạn sẽ phải vượt qua đó để các nhà xây dựng chức năng:
>>> from types import FunctionType
>>> FunctionType(make_adder.__code__.co_consts[1], globals(),
... None, None, (5,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: arg 5 (closure) expected cell, found int
nhưng khi bạn có thể thấy, các nhà xây dựng hy vọng sẽ tìm thấy một đóng cửa, không phải là một giá trị số nguyên. Để tạo ra một đóng cửa, chúng ta cần, một hàm có các biến tự do; những người được trình biên dịch đánh dấu là có sẵn để đóng lại. Và nó cần phải trả lại những giá trị đóng trên giá trị cho chúng ta, không thể tạo ra một đóng cửa bằng cách khác. Do đó, chúng ta tạo một hàm lồng nhau chỉ để tạo ra một đóng cửa:
def make_closure_cell(val):
def nested():
return val
return nested.__closure__[0]
cell = make_closure_cell(5)
Bây giờ chúng ta có thể tái tạo adder()
mà không gọi make_adder
:
>>> adder = FunctionType(make_adder.__code__.co_consts[1], globals(),
... None, None, (cell,))
>>> adder(10)
15
Có lẽ chỉ gọi make_adder()
sẽ được đơn giản hơn.
Ngẫu nhiên, như bạn có thể thấy, hàm là đối tượng hạng nhất trong Python. make_adder
là một đối tượng và bằng cách thêm (somearguments)
bạn gọi hoặc gọi chức năng. Trong trường hợp này, hàm đó trả về đối tượng hàm khác, một đối tượng mà bạn cũng có thể gọi. Trong ví dụ quanh co ở trên về cách tạo adder()
mà không cần gọi make_adder()
, tôi đã gọi đối tượng hàm make_adder
mà không gọi nó; để tháo rời mã byte Python gắn với nó, hoặc để lấy các hằng số hoặc các bao đóng từ nó, ví dụ.Trong cùng một cách, hàm make_adder()
trả về đối tượng hàm adder
; các điểm của make_adder()
là để tạo ra chức năng đó cho một cái gì đó khác để sau này gọi nó.
Phiên trên được tiến hành với khả năng tương thích giữa Python 2 và 3 trong đầu. Các phiên bản Python 2 cũ hơn hoạt động theo cùng một cách, mặc dù một số chi tiết khác nhau một chút; một số thuộc tính có tên khác, chẳng hạn như func_code
thay vì __code__
chẳng hạn. Tra cứu tài liệu trên các tài liệu này trong các số inspect
module và Python datamodel nếu bạn muốn biết chi tiết về các chi tiết có liên quan đến nitty.
tương đương: 'make_adder = lambda x: lambda y: x + y' bây giờ, bạn có thể gọi bên trong' lambda' không? – Elazar
vâng! 'make_adder = make_adder (...)', sau đó 'make_adder (...)' – ovrwngtvity
Đó là, không, bạn không thể. – Elazar