Trình tự thực hiện trang trí


93
def make_bold(fn):
    return lambda : "<b>" + fn() + "</b>"

def make_italic(fn):
    return lambda : "<i>" + fn() + "</i>"

@make_bold
@make_italic
def hello():
  return "hello world"

helloHTML = hello()

Đầu ra: "<b><i>hello world</i></b>"

Tôi hiểu đại khái về decorator và cách nó hoạt động với một trong số nó trong hầu hết các ví dụ.

Trong ví dụ này, có 2 trong số đó. Từ đầu ra, có vẻ như nó @make_italicthực thi trước, sau đó @make_bold.

Điều này có nghĩa là đối với các chức năng được trang trí, trước tiên nó sẽ chạy chức năng đó trước rồi chuyển dần lên trên cùng cho các trình trang trí khác? Giống như @make_italiclần đầu tiên sau đó @make_bold, thay vì ngược lại.

Vì vậy, điều này có nghĩa là nó khác với tiêu chuẩn của cách tiếp cận từ trên xuống trong hầu hết các ngôn ngữ lập trình? Chỉ cho trường hợp này của người trang trí? Hoặc là tôi sai?


4
vâng nó bắt đầu từ lên đáy đi qua kết quả tiếp theo
Padraic Cunningham

1
Bình luận của @PadraicCunningham cũng là một phần quan trọng của câu trả lời. Đã xảy ra sự cố liên quan ( stackoverflow.com/questions/47042196/… )
hét

Tôi muốn nói nó vẫn còn trên xuống, theo nghĩa là a(b(x))là từ trên xuống dưới (nếu bạn tưởng tượng chia rằng hơn 3 lines)
joel

Câu trả lời:


126

Người trang trí bao hàm chức năng mà họ đang trang trí. Vì vậy, make_boldtrang trí kết quả của make_italictrang trí, mà trang trí hellochức năng.

Các @decoratorcú pháp là đường thực sự chỉ là cú pháp; sau đây:

@decorator
def decorated_function():
    # ...

thực sự được thực thi như:

def decorated_function():
    # ...
decorated_function = decorator(decorated_function)

thay thế decorated_functionđối tượng ban đầu bằng bất cứ thứ gì được decorator()trả về.

Trình trang trí xếp chồng lặp lại quá trình đó ra bên ngoài .

Vì vậy, mẫu của bạn:

@make_bold
@make_italic
def hello():
  return "hello world"

có thể được mở rộng thành:

def hello():
  return "hello world"
hello = make_bold(make_italic(hello))

Khi bạn gọi hello()ngay bây giờ, bạn đang gọi đối tượng được trả về make_bold(), thực sự. make_bold()trả về a lambdagọi hàm make_boldđược bao bọc, là giá trị trả về của make_italic(), cũng là lambda gọi hàm ban đầu hello(). Mở rộng tất cả các cuộc gọi này mà bạn nhận được:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
    lambda : "<i>" + fn() + "</i>" # where fn() -> 
        return "hello world"

vì vậy đầu ra trở thành:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"

Tôi hiểu. Nhưng điều này có nghĩa là khi có 2 wrapper trong trường hợp này, IDE sẽ tự động phát hiện và kết quả của wrapper đầu tiên? Vì tôi đã nghĩ như @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)vậy? Tôi không chắc nếu dựa trên điều này, nó sẽ bao gồm kết quả đầu tiên. Hay đối với trường hợp 2 wrappers này, IDE sẽ sử dụng make_bold(make_italic(hello))như bạn đã đề cập thay vì những gì tôi đã chia sẻ?
Thành viên mới

3
@Newbie: IDE của bạn không làm gì ở đây; nó là Python thực hiện gói. Tôi đã cho bạn thấy trong mẫu cuối cùng của tôi make_bold()bao bọc đầu ra make_italic(), được sử dụng để bọc hello, do đó, tương đương với make_bold(make_italic(hello)).
Martijn Pieters

Bạn có thể cung cấp phiên bản của mã này mà không sử dụng lambda không? Tôi đã thử .format nhưng không hoạt động. Và tại sao lambda được sử dụng trong ví dụ này? Tôi đang cố gắng hiểu lambda và cách nó hoạt động trong ví dụ này nhưng vẫn gặp sự cố. Tôi hiểu rằng lambda giống như một hàm dòng có thể được truyền dễ dàng hơn nhiều so với tiêu chuẩn của các hàm def?
Thành viên mới

def inner: return "<b>" + fn() + "</b>", sau đó return innersẽ là phiên bản hàm 'thông thường'; không phải là một sự khác biệt lớn.
Martijn Pieters

Tôi luôn bối rối về thứ tự. "... decorator sẽ được áp dụng bắt đầu bằng câu lệnh gần nhất với câu lệnh" def "" Tôi gọi đây là "từ trong ra ngoài". Tôi nghĩ Martijn gọi điều này là "hướng ngoại". Điều này có nghĩa là make_italic decorator được thực thi trước make_bold decorator , vì make_italicgần với def. Tuy nhiên, tôi quên rằng thứ tự thực thi mã được trang trí : make_bold trang trí (tức là lambda đậm) được thực hiện đầu tiên, tiếp theo là lambda make_italic được trang trí (tức lambda nghiêng).
The Red Pea,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.