@
เป็นสัญลักษณ์ที่เอาไว้สร้างสิ่งที่เรียกว่าเดคอเรเตอร์ (decorator)def yaki(x):
return '~~' + x + '~~'
~~
ขนาบซ้ายขวาmochi = 'โมจิ'
print
ออกมาดูความแตกต่างระหว่างตัวเดิมกับตัวที่ได้จากฟังก์ชัน
print(mochi) # ได้ โมจิ
print(yaki(mochi)) # ได้ ~~โมจิ~~
mochi
นั้นถูกตกแต่งเพิ่มเติมเข้าไป มีความเปลี่ยนแปลงเกิดขึ้นmochi
เองนั้นไม่ได้มีค่าเปลี่ยนแปลงไป แค่รับเอาค่าไปแต่งเติมเท่านั้น หากอยากให้มันเปลี่ยนแปลงก็จะต้องแทนค่าที่ได้ใหม่กลับเข้าไปยังตัวมัน
mochi = yaki(mochi)
print(mochi) # ได้ ~~โมจิ~~
mochi = yaki(mochi)
print(mochi) # ได้ ~~~~โมจิ~~~~
mochi
ถูกแต่งเติมให้เปลี่ยนแปลงไปเรื่อยๆด้วยฟังก์ชัน yaki
def yaki(x):
def mochi():
return '~~' + x + '~~'
return mochi
yakimochi = yaki('โมจิย่าง')
print(yakimochi()) # ได้ ~~โมจิย่าง~~
yakimochi = yaki('焼き餅')
print(yakimochi()) # ได้ ~~焼き餅~~
yaki
ทำหน้าที่สร้างฟังก์ชัน mochi
ซึ่งเป็นฟังก์ชันที่จะคืนค่าตามที่ใส่ลงไปเป็นอาร์กิวเมนต์ในฟังก์ชัน yaki
โดยจะมีการแต่งเพิ่มเติมโดยใส่ ~~
ขนาบซ้ายขวาyaki
ในที่นี้ก็ยังไม่ถือว่าเป็นเดคอเรเตอร์ เพราะแค่รับค่าสายอักขระเข้าไปแล้วส่งฟังก์ชันออกมา แต่เดคอเรเตอร์จะต้องสามารถรับฟังก์ชันได้ แล้วส่งฟังก์ชันที่ถูกแต่งเติมไปจากเดิมกลับออกมาdef deko(f):
def boko():
return '~*@*^*' + f() + '*^*@*~'
return boko
def kira():
return 'kirakira'
print(kira()) # ได้ kirakira
kira = deko(kira)
print(kira()) # ได้ ~*@*^*kirakira*^*@*~
deko
ในที่นี้คล้ายกับฟังก์ชัน yaki
ในตัวอย่างที่แล้วมาก()
อยู่หลัง f
(ไม่รวมการที่เปลี่ยนชื่อตัวแปรจาก f
เป็น x
และส่วนแต่งเติมที่ดูฉูดฉาดขึ้นกว่าเดิม)kira = deko(kira)
นั้นจะเห็นว่าฟังก์ชัน deko
รับฟังก์ชัน kira
ไปเป็นอาร์กิวเมนต์ ดังนั้น f
จะถูกแทนด้วย kira
boko
ก็เอา kira
นี้มาใช้ โดยเติม ()
เข้าไปก็เท่ากับว่าทำให้ฟังก์ชันทำงาน ซึ่งจะคืนค่าสายอักขระ kirakira
กลับออกมา จากนั้นจึงไปบวกกับส่วนแต่งเติมที่ขนาบข้างอยู่แล้วจึงคืนค่าออกมาในที่สุดreturn boko
นั้น ฟังก์ชัน boko
นี้จะกลายเป็นค่าคืนกลับของฟังก์ชัน deko
kira
ซึ่งเดิมทีเป็นฟังก์ชันที่จะคืนค่าสายอักขระ kirakira
แต่พอผ่านกระบวนการนี้มันก็จะกลายเป็นฟังก์ชันที่คืนค่าสายอักขระ ~*@*^*kirakira*^*@*~
ไปแล้วkirameki = deko(kira)
print(kirameki()) # ได้ ~*@*^*kirakira*^*@*~
kirameki
ก็จะกลายเป็นฟังก์ชัน kira
ในเวอร์ชันที่ถูกแต่งเติมไปแล้วdeko
เป็นเดคอเรเตอร์ ส่วน kira
เป็นฟังก์ชันที่ถูกตกแต่ง@
ซึ่งได้เกริ่นถึงไว้ในตอนแรกเลย@
นั้นเป็นเพียงสิ่งที่เรียกว่าน้ำตาลทางวากยสัมพันธ์ (syntax sugar หรือ syntactic sugar) คือไวยากรณ์ที่ถูกออกแบบมาเป็นพิเศษเพื่อให้ดูแล้วเรียบง่ายขึ้น ดูแล้วเข้าใจง่ายขึ้น ไม่ต้องเขียนให้ยืดยาว@
เป็นเครื่องหมายที่เอาไว้ใช้เขียนนำหน้าฟังก์ชันที่ต้องการใช้เป็นเดคอเรเตอร์ แล้วนำไปวางไว้ด้านบนฟังก์ชันที่ต้องการตกแต่งอีกที@
แทนได้ดังนี้
def deko(f):
def boko():
return '~*@*^*' + f() + '*^*@*~'
return boko
@deko
def kira():
return 'kirakira'
print(kira()) # ได้ ~*@*^*kirakira*^*@*~
kira = deko(kira)
หายไปแล้ว นั่นเพราะถูกแทนด้วย @deko
ที่วางเพิ่มเข้ามาก่อน def kira
@
ที่ทำให้นักเขียนโปรแกรมมากมายที่เพิ่งหัดภาษาไพธอนต้องงงงวยกันพอสมควรในการทำความเข้าใจ@
ทำเป็นเพียงแค่การย่อโค้ดให้ดูสั้นลงเล็กน้อยเท่านั้น@
แล้วก็จะรู้สึกว่าใช้แล้วดูเรียบง่ายขึ้น@
กับไม่ใช้@
ซึ่งจะได้เป็น
def deko(f):
def boko(x):
return '~*@*^*' + f(x) + '*^*@*~'
return boko
def kira(x):
return x
kira = deko(kira)
print(kira('คิระคิระ')) # ได้ ~*@*^*คิระคิระ*^*@*~
@
ก็จะเป็น
def deko(f):
def boko(x):
return '~*@*^*' + f(x) + '*^*@*~'
return boko
@deko
def kira(x):
return x
print(kira('คิระคิระ')) # ได้ ~*@*^*คิระคิระ*^*@*~
kira
คราวนี้มีพารามิเตอร์ x
เข้ามา ทำให้ต้องใส่อาร์กิวเมนต์เข้าไปเวลาเรียกใช้ฟังก์ชันด้วยboko
ก็ต้องเพิ่มพารามิเตอร์เข้ามาด้วย ดังที่เห็นdeko
นี้จะเอามาใช้กับฟังก์ชันอะไรก็ได้ที่รับค่าพารามิเตอร์เป็นสายอักขระตัวเดียว
@deko
def kirakirakira(x):
return x*3
print(kirakirakira('คิระ')) # ได้ ~*@*^*คิระคิระคิระ*^*@*~
@deko
def kirameki(x,y):
return x+y
print(kirameki('คิระ','เมคิ')) # ได้ TypeError: boko() takes 1 positional argument but 2 were given
deko
ให้รองรับพารามิเตอร์
def deko(f):
def boko(x,y):
return '~*@*^*' + f(x,y) + '*^*@*~'
return boko
*
เป็นพารามิเตอร์
def deko(f):
def boko(*arg):
return '~*@*^*' + f(*arg) + '*^*@*~'
return boko
@deko
def kirameki(x,y): # พารามิเตอร์ ๒ ตัว
return x+y
@deko
def kirakira(x): # พารามิเตอร์ ๑ ตัว
return x*2
print(kirameki('คิระ','เมคิ')) # ได้ ~*@*^*คิระเมคิ*^*@*~
print(kirakira('คิระ')) # ได้ ~*@*^*คิระคิระ*^*@*~
deko
ตอนนี้สามารถใช้งานได้กับฟังก์ชันที่มีพารามิเตอร์กี่ตัวก็ได้*
เป็นพารามิเตอร์เพื่อให้รองรับฟังก์ชันที่มีจำนวนพารามิเตอร์กี่ตัวก็ได้def décorateur(f):
def g(*arg,**kw):
print('อาร์กิวเมนต์: %s คีย์เวิร์ด: %s'%(arg,kw))
return f(*arg,**kw)
return g
@décorateur
def kiraku(a,b,c,d='ง'):
return a,b,c,d
@décorateur
def kirabiyaka(x,y,z=7):
return x,y,z
kiraku('ก','ข',c='ค') # ได้ อาร์กิวเมนต์: ('ก', 'ข') คีย์เวิร์ด: {'c': 'ค'}
kirabiyaka(1,y=2,z=5) # ได้ อาร์กิวเมนต์: (1,) คีย์เวิร์ด: {'z': 5, 'y': 2}
décorateur
เป็นเดคอเรเตอร์ที่ทำหน้าที่แต่งให้ฟังก์ชันใดๆมีการ print
ค่าอาร์กิวเมนต์และคีย์เวิร์ดทั้งหมดที่ถูกใส่เข้ามาเมื่อฟังก์ชันถูกเรียกใช้งานg
มีพารามิเตอร์เป็น *arg
และ **kw
และ ซึ่งถูกนำมาแสดงผลใน print
*arg
แทนอาร์กิวเมนต์ทั้งหมด และ **kw
แทนคีย์เวิร์ดทั้งหมด ดังนั้นมันจึงครอบคลุมทั้งอาร์กิวเมนต์และคีย์เวิร์ดทั้งหมดที่ถูกใช้ใน ฟังก์ชันที่จะถูกแต่งเติมarg
และ kw
ไม่ได้ถูกใช้เพื่อทำอะไรนอกจาก print
ดังนั้นไม่ว่าอาร์กิวเมนต์และคีย์จะเป็นข้อมูลชนิดใดก็ตามก็ไม่ทำให้โปรแกรมขัดข้องdécorateur
นี้ไม่ได้ทำอะไรเพิ่มเติมนอกจาก print
อาร์กิวเมนต์และคีย์เวิร์ดของฟังก์ชันที่ถูกแต่ง ดังนั้นมันจึงเป็นเดคอเรเตอร์ที่ใช้กับฟังก์ชันอะไรก็ได้@
ในแบบที่อ่านแล้วอาจทำให้งงกว่าเดิมจึงอยากจะเน้นย้ำ@
อาจไม่จำเป็นต้องคืนค่าเป็นฟังก์ชันหรือคลาสเสมอไป แต่อาจคืนออบเจ็กต์อะไรบางอย่างที่แตกต่างจากสิ่งเดิมออกไปสิ้นเชิงเลยก็เป็นได้def kirari():
return '^คิราริ^'
print(kirari()) # ได้ ^คิราริ^
()
ไปข้างหลัง ตรงนี้ดูแล้วก็เป็นเรื่องปกติ ไม่มีปัญหาอะไร@
def dekomori(f):
return '^เดโกโมริ^'
@dekomori
def kirari():
return '^คิราริ^'
print(kirari()) # ได้ TypeError: 'str' object is not callable
kirari
ในกรณีนี้? ในเมื่อเรานิยาม kirari
ด้วย def
มันก็ควรเป็นฟังก์ชัน แต่ทำไมถึงเกิดข้อผิดพลาดโดยขึ้นเตือนว่า kirari
เป็นสายอักขระ@
ดู จะได้ว่า
def kirari():
return '^คิราริ^'
kirari = dekomori(kirari)
kirari
เริ่มถูกนิยามด้วย def
ให้เป็นฟังก์ชัน แต่ต่อมันถูกเขียนทับใหม่ด้วยฟังก์ชัน dekomori
kirari
ซึ่งเดิมทีเป็นฟังก์ชันจึงถูกเขียนทับกลายเป็นสายอักขระนั่นเอง สายอักขระไม่สามารถเติมวงเล็บ ()
ด้านหลังเพื่อให้ทำงานได้ ดังนั้นจึงขัดข้องprint
ใหม่โดยตัด ()
ข้างหลังออกก็จะได้ข้อความจากสายอักขระออกมา
print(kirari) # ได้ ^เดโกโมริ^
@
แล้วละก็เจอโปรแกรมแบบนี้ก็คงจะงงและยากที่จะเข้าใจได้def dekomori(f):
return f('คิราเดโกะ')
@dekomori
def kirari(x):
return 'คิราริ'+x
print(kirari) # ได้ คิราริคิราเดโกะ
ในตัวอย่างนี้ dekomori
เป็นเดคอเรเตอร์ที่นำเอาฟังก์ชันมาแล้วป้อน 'คิราเดโกะ'
เป็นอาร์กิวเมนต์ใส่เข้าไป จากนั้นก็คืนค่าที่ได้จากฟังก์ชันออกมาโดยตรงเลยdef kiradeko(f):
def dekodeko():
return f('เดโกะเดโกะ')
def kirakira():
return f('คิระคิระ')
return (dekodeko,kirakira)
@kiradeko
def kirari(x):
return 'คิราริ' + x
print(kirari) # ได้ (.dekodeko at 0x1123529d8>, .kirakira at 0x112352d08>)
print(kirari[0]()) # ได้ คิราริเดโกะเดโกะ
print(kirari[1]()) # ได้ คิราริคิระคิระ
ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ