def dekoboko(f):
def g():
return f() + ' dekoboko'
return g
def dekopin(f):
def g():
return f() + ' dekopin'
return g
@dekoboko
@dekopin
def odeko():
return 'odeko'
print(odeko()) # ได้ odeko dekopin dekoboko
dekoboko กับ dekopin จากนั้นทั้ง ๒ อันถูกนำมาใช้เพื่อตกแต่งฟังก์ชัน odeko โดยวางซ้อนกันไปdef odeko():
return 'odeko'
odeko = dekoboko(dekopin(odeko))
odeko = dekopin(odeko)
odeko = dekoboko(odeko)
odeko ถูกตกแต่งด้วย dekopin ก่อน จากนั้นจึงถูกตกแต่งด้วย dekoboko ตามมาodeko ซึ่งเดิมทีจะคืนค่าแค่คำว่า odeko ก็จะถูกเติมคำว่า dekopin จากนั้นก็ถูกเติมคำว่า dekoboko ตามลำดับdef dekopin():
def pin(f):
def g():
return f() + ' dekopin'
return g
return pin
@dekopin()
def odeko():
return 'odeko'
print(odeko()) # odeko dekopin
@dekopin() มีวงเล็บอยู่ข้างหลังแทนที่จะใส่แค่ชื่อฟังก์ชันทำให้ดูแล้วแปลกตา และแค่เห็นฟังก์ชัน dekopin มี def ซ้อนกัน ๓ ชั้นแบบนี้ก็คงจะรู้สึกลำบากใจแล้ว แต่มาลองค่อยๆไล่ดูไปทีละนิดdekopin หากเขียนโดยไม่ใช่ @ แล้วก็จะกลายเป็น
def odeko():
return 'odeko'
odeko = dekopin()(odeko)
dekopin มีวงเล็บตามอยู่ข้างหลังอันหนึ่งก่อนที่จะเป็นวงเล็บที่มี odeko หมายความว่ามันจะทำงานในฐานะฟังก์ชันทันทีก่อน และผลที่ได้ก็คือจะคืนค่าออกมา ซึ่งในที่นี้จะได้ว่าคืนค่าฟังก์ชัน pin ออกมาodeko = pin(odeko)
@pin
def odeko():
return 'odeko'
pin ที่อยู่ข้างใน dekopin อีกทีdekopin ต้องการพารามิเตอร์ในวงเล็บก็ต้องใส่ตาม เช่น
def dekopin(x):
def pin(f):
def g(y):
return f(y)+x
return g
return pin
@dekopin('pin')
def odeko(s):
return s
print(odeko('dekodeko')) # ได้ dekodekopin
def deko(f):
def neko():
'<<นี่เป็นฟังก์ชันที่ถูกสร้างขึ้นภายใน deko>>'
return f() + 'neko'
return neko
@deko
def koko():
'<<นี่คือฟังก์ชันที่ชื่อว่า koko>>'
return 'konoko'
print(koko()) # ได้ konokoneko
print(koko) # ได้ .neko at 0x11251c0d0>
print(koko.__name__) # ได้ neko
print(koko.__doc__) # ได้ <<นี่เป็นฟังก์ชันที่ถูกสร้างขึ้นภายใน deko>>
deko เพื่อทำการตกแต่งฟังก์ชัน kokokoko ที่ถูกแต่งแล้วก็คือได้ข้อความ konokoneko นั้นเป็นไปตามที่ต้องการไม่มีปัญหาnekonekokoko = deko(koko)
deko นั้น return กลับมาก็คือฟังก์ชัน neko เลยกลายเป็นว่าฟังก์ชันชื่อ neko ไปอยู่ในตัวแปรชื่อ kokoneko นี้จะสืบทอดคุณสมบัติต่างๆของ koko แต่ว่าชื่อฟังก์ชันและด็อกสตริงไม่ได้สืบทอดมาด้วยneko.__doc__ = f.__doc__ ไว้ก่อน return neko แบบนี้เราก็จะได้ด็อกสตริงของฟังก์ชันเดิม แต่นั่นก็ยังไม่ใช่การแก้ปัญหาที่เด็ดขาดถูกทางwraps หรือฟังก์ชัน update_wrapper ซึ่งอยู่ในมอดูล functoolsfunctools เป็นมอดูลที่รวบรวมฟังก์ชันที่เอาไว้จัดการเกี่ยวกับฟังก์ชัน มีฟังก์ชันหลายตัวที่มีประโยชน์ แต่ในที่นี้จะพูดถึง ๒ ตัวซึ่งมักใช้คู่กับเดคอเรเตอร์update_wrapper ก่อน เราสามารถใช้ฟังก์ชันนี้เพื่อแก้ฟังก์ชัน deko ให้เป็นแบบนี้
import functools
def deko(f):
def neko():
'<<นี่เป็นฟังก์ชันที่ถูกสร้างขึ้นภายใน deko>>'
return f() + 'neko'
return functools.update_wrapper(neko,f)
deko เดิมก็มีเพียงแค่ return neko เปลี่ยนเป็น return functools.update_wrapper(neko,f) เท่านั้น__doc__ และ __name__ ของฟังก์ชันที่ใส่เป็นอาร์กิวเมนต์ตัวแรกแทนที่ด้วยของอาร์กิวเมนต์ตัวหลังneko) และอาร์กิวเมนต์ตัวหลังคือฟังก์ชันที่ต้องการตกแต่ง (ในที่นี้คือ f)neko ที่ถูกแก้แล้ว จากนั้นเราก็นำค่านี้มาใส่ใน return แทนที่จะใส่ neko เฉยๆแบบตอนแรกdeko แล้วจากนั้นลองดูใหม่คราวนี้จะได้ผลตามที่ต้องการ
print(koko) # ได้
print(koko.__doc__) # ได้ <<นี่คือฟังก์ชันที่ชื่อว่า koko>>
wraps ก็จะเขียนคล้ายๆกัน
import functools
def deko(f):
def neko():
'<<นี่เป็นฟังก์ชันที่ถูกสร้างขึ้นภายใน deko>>'
return f() + 'neko'
return functools.wraps(f)(neko)
functools.update_wrapper(neko,f) เป็น functools.wraps(f)(neko) เท่านั้น ส่วนการทำงานก็เช่นเดิมfunctools.wraps(f)(neko) แทน neko นั้นก็เท่ากับว่า functools.wraps(f) ทำตัวเป็นเดคอเรเตอร์ที่ตกแต่ง nekoimport functools
def deko(f):
@functools.wraps(f)
def neko():
'<<นี่เป็นฟังก์ชันที่ถูกสร้างขึ้นภายใน deko>>'
return f() + 'neko'
return neko
@functools.wraps(f) ลงไปเท่านั้นwraps หรือ update_wrapper จะทำให้เดคอเรเตอร์ดูสมบูรณ์แบบยิ่งขึ้น ดังนั้นอาจถูกใช้เป็นประจำเวลาสร้างฟังก์ชันสำหรับใช้เป็นเดคอเรเตอร์ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ