คำสั่งการดำเนินการของมัณฑนากร


96
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()

เอาท์พุต: "<b><i>hello world</i></b>"

ฉันเข้าใจคร่าวๆเกี่ยวกับมัณฑนากรและวิธีการทำงานกับหนึ่งในตัวอย่างส่วนใหญ่

ในตัวอย่างนี้มี 2 ตัว จากการส่งออกมันก็ดูเหมือนว่ารันแรกแล้ว@make_italic@make_bold

หมายความว่าสำหรับฟังก์ชั่นตกแต่งขั้นแรกจะเรียกใช้ฟังก์ชันก่อนจากนั้นจึงเลื่อนไปด้านบนสำหรับมัณฑนากรอื่น ๆ หรือไม่? ชอบ@make_italicก่อนแล้ว@make_boldแทนที่จะตรงกันข้าม

นั่นหมายความว่ามันแตกต่างจากบรรทัดฐานของวิธีการจากบนลงล่างในภาษาการเขียนโปรแกรมส่วนใหญ่? สำหรับมัณฑนากรกรณีนี้เท่านั้นหรือ หรือว่าฉันผิด?


4
ใช่มันเริ่มจากล่างขึ้นบนส่งผลไปยังถัดไป
Padraic Cunningham

1
ความคิดเห็น @PadraicCunningham เป็นส่วนสำคัญของคำตอบเช่นกัน มีปัญหาที่เกี่ยวข้อง ( stackoverflow.com/questions/47042196/… )
ฉาย

ฉันจะบอกว่ามันยังอยู่บนลงล่างในความหมายที่a(b(x))อยู่บนลงล่าง (ถ้าคุณนึกภาพว่าแบ่งมากกว่า 3 บรรทัด)
joel

คำตอบ:


131

มัณฑนากรห่อหุ้มฟังก์ชันที่กำลังตกแต่ง ดังนั้นการmake_boldตกแต่งผลของmake_italicมัณฑนากรซึ่งตกแต่งhelloฟังก์ชั่น

@decoratorไวยากรณ์น้ำตาลจริงๆเพียงแค่ประโยค; ดังต่อไปนี้:

@decorator
def decorated_function():
    # ...

ถูกดำเนินการเป็น:

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

แทนที่decorated_functionวัตถุเดิมด้วยสิ่งที่decorator()ส่งคืน

Stacking ตกแต่งซ้ำกระบวนการที่ออกไปด้านนอก

ดังนั้นตัวอย่างของคุณ:

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

สามารถขยายเป็น:

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

เมื่อคุณโทรhello()ตอนนี้คุณกำลังเรียกวัตถุที่ส่งคืนมาmake_bold()จริงๆ make_bold()กลับlambdaที่เรียกฟังก์ชั่นmake_boldห่อซึ่งเป็นค่าตอบแทนของซึ่งยังเป็นแลมบ์ดาที่เรียกร้องเดิมmake_italic() hello()ขยายการโทรทั้งหมดที่คุณได้รับ:

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

ดังนั้นผลลัพธ์จะกลายเป็น:

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

ฉันเข้าใจ. แต่หมายความว่าเมื่อมี 2 wrapper ในกรณีนี้ IDE จะตรวจจับและตัดผลลัพธ์ของ wrapper แรกโดยอัตโนมัติหรือไม่? เพราะคิดว่า @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)? ฉันไม่แน่ใจว่าจากนี้มันจะรวมผลแรกหรือไม่ หรือสำหรับกรณีของ 2 wrapper นี้ IDE จะใช้make_bold(make_italic(hello))ตามที่คุณได้กล่าวถึงแทนสิ่งที่ฉันแชร์?
Newbie

3
@ มือใหม่: IDE ของคุณไม่ได้ทำอะไรเลยที่นี่ เป็นPythonที่ทำการตัด ฉันแสดงให้คุณเห็นในตัวอย่างสุดท้ายของฉันซึ่งmake_bold()รวมเอาท์พุทของmake_italic()ซึ่งใช้ในการห่อhelloดังนั้นจึงเทียบเท่ากับmake_bold(make_italic(hello)).
Martijn Pieters

คุณสามารถระบุรหัสรุ่นนี้โดยไม่ต้องใช้แลมด้าได้หรือไม่? ฉันได้ลอง. format แล้ว แต่ไม่ได้ผล และเหตุใดจึงใช้ lambda ในตัวอย่างนี้? ฉันพยายามทำความเข้าใจแลมด้าและวิธีการทำงานในตัวอย่างนี้ แต่ยังคงมีปัญหา ฉันเข้าใจว่าแลมบ์ดาเป็นเหมือนฟังก์ชันบรรทัดเดียวที่สามารถส่งผ่านได้ง่ายมากเมื่อเทียบกับบรรทัดฐานของฟังก์ชัน def?
Newbie

def inner: return "<b>" + fn() + "</b>"จากนั้นreturn innerจะเป็นเวอร์ชันฟังก์ชัน "ปกติ" ไม่ใช่ความแตกต่างที่ยิ่งใหญ่
Martijn Pieters

ฉันมักจะสับสนเกี่ยวกับคำสั่งซื้อ "... นักตกแต่งจะถูกนำไปใช้โดยเริ่มจากคนที่ใกล้เคียงที่สุดกับคำสั่ง" def "" ฉันเรียกสิ่งนี้ว่า "inside-out" ฉันคิดว่า Martijn เรียกสิ่งนี้ว่า "ภายนอก" ซึ่งหมายความว่าmake_italic มัณฑนากรจะดำเนินการก่อนmake_bold มัณฑนากรเนื่องจากmake_italicอยู่ใกล้กับไฟล์def. อย่างไรก็ตามฉันลืมว่าคำสั่งประมวลผลโค้ดที่ตกแต่งแล้ว : การmake_bold ตกแต่ง (เช่นแลมด้าตัวหนา) จะถูกดำเนินการก่อนตามด้วยแลมด้าที่make_italic ตกแต่งแล้ว (เช่นแลมด้าตัวเอียง)
Red Pea
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.