Python decorator เป็นโครงสร้างที่มีประโยชน์อย่างเหลือเชื่อใน Python การใช้มัณฑนากรใน Python เราสามารถปรับเปลี่ยนพฤติกรรมของฟังก์ชันได้โดยการรวมไว้ในฟังก์ชันอื่น นักตกแต่งช่วยให้เราเขียนโค้ดที่สะอาดขึ้นและแบ่งปันฟังก์ชันการทำงานได้ บทความนี้เป็นบทช่วยสอนเกี่ยวกับวิธีการใช้มัณฑนากร แต่ยังรวมถึงวิธีสร้างมันด้วย
ความรู้เบื้องต้น
หัวข้อของนักตกแต่งใน Python ต้องการความรู้พื้นฐานบางอย่าง ด้านล่างนี้ ฉันได้แสดงรายการแนวคิดบางอย่างที่คุณควรคุ้นเคยเพื่อให้เข้าใจถึงบทช่วยสอนนี้ ฉันยังได้เชื่อมโยงแหล่งข้อมูลซึ่งคุณสามารถทำความเข้าใจกับแนวคิดต่างๆ ได้หากจำเป็น
ไพธอนพื้นฐาน
หัวข้อนี้เป็นหัวข้อขั้นกลาง/ขั้นสูง ด้วยเหตุนี้ ก่อนที่จะพยายามเรียนรู้ คุณควรคุ้นเคยกับพื้นฐานของ Python เช่น ชนิดข้อมูล ฟังก์ชัน ออบเจกต์ และคลาส
คุณควรเข้าใจแนวคิดเชิงวัตถุบางอย่าง เช่น ตัวรับ ตัวตั้ง และตัวสร้าง หากคุณไม่คุ้นเคยกับภาษาโปรแกรม Python ต่อไปนี้เป็นแหล่งข้อมูลบางส่วนที่จะช่วยคุณเริ่มต้น
ฟังก์ชั่นเป็นพลเมืองชั้นหนึ่ง
นอกจาก Python พื้นฐานแล้ว คุณควรทราบแนวคิดขั้นสูงกว่านี้ใน Python ด้วย ฟังก์ชันและเกือบทุกอย่างใน Python เป็นออบเจกต์เช่น int หรือ string เนื่องจากพวกมันเป็นวัตถุ คุณจึงทำบางสิ่งกับพวกมันได้ กล่าวคือ:
- คุณสามารถส่งฟังก์ชันเป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่นได้ด้วยวิธีเดียวกับที่คุณส่งสตริงหรือ int เป็นอาร์กิวเมนต์ของฟังก์ชัน
- ฟังก์ชันสามารถส่งคืนโดยฟังก์ชันอื่นๆ เช่น คุณจะส่งคืนค่าสตริงหรือ int อื่นๆ
- ฟังก์ชั่นสามารถเก็บไว้ในตัวแปร
ในความเป็นจริง ข้อแตกต่างเพียงอย่างเดียวระหว่างอ็อบเจกต์เชิงฟังก์ชันกับอ็อบเจ็กต์อื่นๆ คืออ็อบเจกต์เชิงฟังก์ชันมีเมธอดเวทมนตร์ __call__()
หวังว่า ณ จุดนี้ คุณจะคุ้นเคยกับความรู้เบื้องต้นแล้ว เราสามารถเริ่มการสนทนาหัวข้อหลัก
Python มัณฑนากรคืออะไร?
Python มัณฑนากรเป็นเพียงฟังก์ชันที่รับฟังก์ชันเป็นอาร์กิวเมนต์และส่งคืนเวอร์ชันแก้ไขของฟังก์ชันที่ส่งผ่านเข้ามา กล่าวคือ ฟังก์ชัน foo เป็นมัณฑนากรหากใช้อาร์กิวเมนต์แถบฟังก์ชัน และส่งคืนฟังก์ชัน baz อื่น
ฟังก์ชัน baz เป็นการดัดแปลงของแถบในแง่ที่ว่าภายในเนื้อความของ baz มีการเรียกไปยังแถบฟังก์ชัน อย่างไรก็ตาม ก่อนและหลังการโทรไปที่บาร์ บาซสามารถทำอะไรก็ได้ นั่นเป็นคำพูด; นี่คือรหัสบางส่วนเพื่อแสดงสถานการณ์:
# Foo is a decorator, it takes in another function, bar as an argument def foo(bar): # Here we create baz, a modified version of bar # baz will call bar but can do anything before and after the function call def baz(): # Before calling bar, we print something print("Something") # Then we run bar by making a function call bar() # Then we print something else after running bar print("Something else") # Lastly, foo returns baz, a modified version of bar return baz
จะสร้างมัณฑนากรใน Python ได้อย่างไร?
เพื่อแสดงให้เห็นว่ามีการสร้างและใช้มัณฑนากรใน Python อย่างไร ฉันจะอธิบายสิ่งนี้ด้วยตัวอย่างง่ายๆ ในตัวอย่างนี้ เราจะสร้างฟังก์ชันตกแต่งคนตัดไม้ซึ่งจะบันทึกชื่อของฟังก์ชันที่กำลังตกแต่งทุกครั้งที่ฟังก์ชันนั้นทำงาน
ในการเริ่มต้น เราสร้างฟังก์ชันมัณฑนากร มัณฑนากรใช้ func เป็นข้อโต้แย้ง func คือฟังก์ชั่นที่เรากำลังตกแต่ง
def create_logger(func): # The function body goes here
ภายในฟังก์ชันมัณฑนากร เราจะสร้างฟังก์ชันที่แก้ไขซึ่งจะบันทึกชื่อฟังก์ชันก่อนเรียกใช้ฟังก์ชัน
# Inside create_logger def modified_func(): print("Calling: ", func.__name__) func()
ถัดไป ฟังก์ชัน create_logger จะส่งคืนฟังก์ชันที่แก้ไขแล้ว ด้วยเหตุนี้ ฟังก์ชัน create_logger ทั้งหมดของเราจะมีลักษณะดังนี้:
def create_logger(func): def modified_func(): print("Calling: ", func.__name__) func() return modified_function
เราทำการสร้างมัณฑนากรเสร็จแล้ว ฟังก์ชัน create_logger เป็นตัวอย่างง่ายๆ ของฟังก์ชันมัณฑนากร มันรับฟังก์ชั่น func ซึ่งเป็นฟังก์ชั่นที่เรากำลังตกแต่งและส่งกลับฟังก์ชั่นอื่น modified_func modified_func จะบันทึกชื่อของ func ก่อน ก่อนที่จะเรียกใช้ func
วิธีใช้มัณฑนากรใน Python
ในการใช้มัณฑนากรของเรา เราใช้ไวยากรณ์ @ ดังนี้:
@create_logger def say_hello(): print("Hello, World!")
ตอนนี้เราสามารถเรียก say_hello() ในสคริปต์ของเรา และผลลัพธ์ควรเป็นข้อความต่อไปนี้:
Calling: say_hello "Hello, World"
แต่ @create_logger กำลังทำอะไรอยู่? กำลังใช้มัณฑนากรกับฟังก์ชัน say_hello ของเรา เพื่อให้เข้าใจสิ่งที่กำลังทำได้ดีขึ้น โค้ดด้านล่างย่อหน้านี้จะให้ผลลัพธ์เหมือนกับการใส่ @create_logger ก่อน say_hello
def say_hello(): print("Hello, World!") say_hello = create_logger(say_hello)
กล่าวอีกนัยหนึ่ง วิธีหนึ่งในการใช้มัณฑนากรใน Python คือการเรียกมัณฑนากรผ่านฟังก์ชันอย่างชัดแจ้งเหมือนที่เราทำในโค้ดด้านบน อีกวิธีหนึ่งที่กระชับกว่าคือการใช้ @ ไวยากรณ์
ในส่วนนี้ เราได้พูดถึงวิธีการสร้างตัวตกแต่ง Python
ตัวอย่างที่ซับซ้อนขึ้นเล็กน้อย
ตัวอย่างข้างต้นเป็นกรณีง่ายๆ มีตัวอย่างที่ซับซ้อนกว่านี้เล็กน้อย เช่น เมื่อฟังก์ชันที่เรากำลังตกแต่งใช้อาร์กิวเมนต์ อีกสถานการณ์ที่ซับซ้อนกว่าคือเมื่อคุณต้องการตกแต่งทั้งชั้นเรียน ฉันจะครอบคลุมทั้งสองสถานการณ์นี้ที่นี่
เมื่อฟังก์ชันใช้อาร์กิวเมนต์
เมื่อฟังก์ชันที่คุณกำลังตกแต่งใช้อาร์กิวเมนต์ ฟังก์ชันที่แก้ไขควรรับอาร์กิวเมนต์และส่งผ่านอาร์กิวเมนต์ในที่สุดเมื่อทำการเรียกไปยังฟังก์ชันที่ไม่ได้แก้ไข หากฟังดูสับสน ให้ฉันอธิบายด้วยคำว่า foo-bar
จำไว้ว่า foo คือฟังก์ชันการตกแต่ง bar คือฟังก์ชันที่เรากำลังตกแต่ง และ baz คือแถบตกแต่ง ในกรณีนั้น bar จะรับข้อโต้แย้งและส่งต่อไปยัง baz ในระหว่างการเรียกไปยัง baz นี่คือตัวอย่างรหัสเพื่อเสริมแนวคิด:
def foo(bar): def baz(*args, **kwargs): # You can do something here ___ # Then we make the call to bar, passing in args and kwargs bar(*args, **kwargs) # You can also do something here ___ return baz
หาก *args และ **kwargs ดูไม่คุ้นเคย เป็นเพียงตัวชี้ไปยังอาร์กิวเมนต์ตำแหน่งและคำหลักตามลำดับ
สิ่งสำคัญคือต้องสังเกตว่า baz สามารถเข้าถึงอาร์กิวเมนต์ได้ ดังนั้นจึงสามารถดำเนินการตรวจสอบอาร์กิวเมนต์บางอย่างก่อนที่จะเรียกบาร์
ตัวอย่างเช่น ถ้าเรามีฟังก์ชันมัณฑนากร, sure_string ที่จะทำให้แน่ใจว่าอาร์กิวเมนต์ที่ส่งไปยังฟังก์ชันที่กำลังตกแต่งนั้นเป็นสตริง; เราจะนำไปใช้ดังนี้:
def ensure_string(func): def decorated_func(text): if type(text) is not str: raise TypeError('argument to ' + func.__name__ + ' must be a string.') else: func(text) return decorated_func
เราสามารถตกแต่งฟังก์ชัน say_hello ได้ดังนี้
@ensure_string def say_hello(name): print('Hello', name)
จากนั้นเราสามารถทดสอบโค้ดโดยใช้สิ่งนี้:
say_hello('John') # Should run just fine say_hello(3) # Should throw an exception
และควรสร้างผลลัพธ์ต่อไปนี้:
Hello John Traceback (most recent call last): File "/home/anesu/Documents/python-tutorial/./decorators.py", line 20, in <module> say hello(3) # should throw an exception File "/home/anesu/Documents/python-tu$ ./decorators.pytorial/./decorators.py", line 7, in decorated_func raise TypeError('argument to + func._name_ + must be a string.') TypeError: argument to say hello must be a string. $0
ตามที่คาดไว้ สคริปต์สามารถพิมพ์ ‘Hello John’ ได้เนื่องจาก ‘John’ เป็นสตริง มีข้อยกเว้นเมื่อพยายามพิมพ์ ‘Hello 3’ เนื่องจาก ‘3’ ไม่ใช่สตริง คุณสามารถใช้มัณฑนากร Sure_string เพื่อตรวจสอบอาร์กิวเมนต์ของฟังก์ชันใดๆ ที่ต้องใช้สตริง
ตกแต่งชั้นเรียน
นอกจากฟังก์ชั่นการตกแต่งแล้ว เรายังสามารถตกแต่งคลาสได้อีกด้วย เมื่อคุณเพิ่มมัณฑนากรให้กับคลาส เมธอดที่ตกแต่งจะแทนที่เมธอดตัวสร้าง/ตัวเริ่มต้นของคลาส (__init__)
กลับไปที่ foo-bar สมมติว่า foo เป็นมัณฑนากรของเรา และ Bar เป็นคลาสที่เรากำลังตกแต่ง จากนั้น foo จะตกแต่ง Bar __init__ สิ่งนี้จะมีประโยชน์หากเราต้องการทำอะไรก่อนที่วัตถุประเภท Bar จะถูกสร้างอินสแตนซ์
ซึ่งหมายความว่ารหัสต่อไปนี้
def foo(func): def new_func(*args, **kwargs): print('Doing some stuff before instantiation') func(*args, **kwargs) return new_func @foo class Bar: def __init__(self): print("In initiator")
เทียบเท่ากับ
def foo(func): def new_func(*args, **kwargs): print('Doing some stuff before instantiation') func(*args, **kwargs) return new_func class Bar: def __init__(self): print("In initiator") Bar.__init__ = foo(Bar.__init__)
ในความเป็นจริง การสร้างอินสแตนซ์ของออบเจกต์ของคลาสบาร์ ซึ่งกำหนดโดยใช้หนึ่งในสองวิธี ควรให้ผลลัพธ์ที่เหมือนกันแก่คุณ:
Doing some stuff before instantiation In initiator
ตัวอย่างมัณฑนากรใน Python
แม้ว่าคุณจะสามารถกำหนดมัณฑนากรของคุณเองได้ แต่ก็มีบางส่วนที่สร้างไว้ใน Python แล้ว นี่คือตัวตกแต่งทั่วไปบางส่วนที่คุณอาจพบใน Python:
@วิธีคงที่
เมธอดสแตติกถูกใช้ในคลาสเพื่อระบุว่าเมธอดที่กำลังตกแต่งนั้นเป็นเมธอดสแตติก เมธอดสแตติกคือเมธอดที่สามารถทำงานได้โดยไม่จำเป็นต้องสร้างอินสแตนซ์ของคลาส ในตัวอย่างโค้ดต่อไปนี้ เราสร้างคลาส Dog ด้วยเปลือกเมธอดแบบสแตติก
class Dog: @staticmethod def bark(): print('Woof, woof!')
ตอนนี้สามารถเข้าถึงวิธีการเห่าได้ดังนี้:
Dog.bark()
และการรันโค้ดจะสร้างผลลัพธ์ต่อไปนี้:
Woof, woof!
ดังที่ได้กล่าวไปแล้วในหัวข้อ How to use Decorators, Decorators สามารถใช้ได้สองวิธี @ ไวยากรณ์ที่กระชับมากขึ้นเป็นหนึ่งในสอง อีกวิธีหนึ่งคือการเรียกฟังก์ชันมัณฑนากรโดยผ่านฟังก์ชันที่เราต้องการตกแต่งเป็นอาร์กิวเมนต์ ความหมายรหัสด้านบนบรรลุสิ่งเดียวกับรหัสด้านล่าง:
class Dog: def bark(): print('Woof, woof!') Dog.bark = staticmethod(Dog.bark)
และเรายังสามารถใช้วิธีเปลือกไม้ได้เหมือนเดิม
Dog.bark()
และมันจะให้ผลลัพธ์เดียวกัน
Woof, woof!
อย่างที่คุณเห็น วิธีแรกนั้นสะอาดกว่าและเห็นได้ชัดว่าฟังก์ชันเป็นฟังก์ชันแบบสแตติกก่อนที่คุณจะเริ่มอ่านโค้ดเสียด้วยซ้ำ สำหรับตัวอย่างที่เหลือ ผมจะใช้วิธีแรก แต่อย่าลืมว่าวิธีที่สองเป็นทางเลือกอีกทางหนึ่ง
@วิธีการเรียน
มัณฑนากรนี้ใช้เพื่อระบุว่าเมธอดที่กำลังตกแต่งเป็นเมธอดคลาส เมธอดคลาสนั้นคล้ายกับเมธอดสแตติกตรงที่ทั้งคู่ไม่ต้องการให้สร้างอินสแตนซ์ของคลาสก่อนจึงจะสามารถเรียกใช้ได้
อย่างไรก็ตาม ความแตกต่างที่สำคัญคือเมธอดคลาสสามารถเข้าถึงแอตทริบิวต์คลาสได้ในขณะที่เมธอดสแตติกไม่สามารถเข้าถึงได้ นี่เป็นเพราะ Python ส่งผ่านคลาสโดยอัตโนมัติเป็นอาร์กิวเมนต์แรกไปยังเมธอดคลาสเมื่อใดก็ตามที่ถูกเรียกใช้ ในการสร้าง class method ใน Python เราสามารถใช้ classmethod decorator
class Dog: @classmethod def what_are_you(cls): print("I am a " + cls.__name__ + "!")
ในการรันโค้ด เราเพียงเรียกเมธอดโดยไม่ต้องสร้างอินสแตนซ์คลาส:
Dog.what_are_you()
และผลลัพธ์คือ:
I am a Dog!
@คุณสมบัติ
มัณฑนากรคุณสมบัติใช้เพื่อติดฉลากเมธอดเป็นตัวตั้งค่าคุณสมบัติ กลับไปที่ตัวอย่าง Dog ของเรา เรามาสร้างเมธอดที่ดึงชื่อ Dog กัน
class Dog: # Creating a constructor method that takes in the dog's name def __init__(self, name): # Creating a private property name # The double underscores make the attribute private self.__name = name @property def name(self): return self.__name
ตอนนี้เราสามารถเข้าถึงชื่อสุนัขได้เหมือนทรัพย์สินทั่วไป
# Creating an instance of the class foo = Dog('foo') # Accessing the name property print("The dog's name is:", foo.name)
และผลลัพธ์ของการรันโค้ดจะเป็นอย่างไร
The dog's name is: foo
@property.setter
มัณฑนากร property.setter ใช้เพื่อสร้างเมธอด setter สำหรับคุณสมบัติของเรา หากต้องการใช้ @property.setter มัณฑนากร คุณต้องแทนที่คุณสมบัติด้วยชื่อของคุณสมบัติ คุณกำลังสร้าง setter สำหรับ ตัวอย่างเช่น หากคุณกำลังสร้าง setter สำหรับเมธอดสำหรับคุณสมบัติ foo มัณฑนากรของคุณจะเป็น @foo.setter นี่คือตัวอย่างสุนัขเพื่ออธิบาย:
class Dog: # Creating a constructor method that takes in the dog's name def __init__(self, name): # Creating a private property name # The double underscores make the attribute private self.__name = name @property def name(self): return self.__name # Creating a setter for our name property @name.setter def name(self, new_name): self.__name = new_name
ในการทดสอบ setter เราสามารถใช้รหัสต่อไปนี้:
# Creating a new dog foo = Dog('foo') # Changing the dog's name foo.name="bar" # Printing the dog's name to the screen print("The dog's new name is:", foo.name)
การรันโค้ดจะสร้างผลลัพธ์ต่อไปนี้:
The dogs's new name is: bar
ความสำคัญของมัณฑนากรใน Python
ตอนนี้เราได้อธิบายว่ามัณฑนากรคืออะไร และคุณได้เห็นตัวอย่างของมัณฑนากรแล้ว เราสามารถพูดคุยว่าทำไมมัณฑนากรจึงมีความสำคัญใน Python มัณฑนากรมีความสำคัญด้วยเหตุผลหลายประการ บางส่วนของพวกเขาฉันได้ระบุไว้ด้านล่าง:
- พวกเขาเปิดใช้งานการนำรหัสกลับมาใช้ใหม่: ในตัวอย่างการบันทึกด้านบน เราสามารถใช้ @create_logger กับฟังก์ชันใดก็ได้ที่เราต้องการ ซึ่งช่วยให้เราสามารถเพิ่มฟังก์ชันการบันทึกลงในฟังก์ชันทั้งหมดของเราโดยไม่ต้องเขียนเองสำหรับแต่ละฟังก์ชัน
- อนุญาตให้คุณเขียนโค้ดโมดูลาร์: อีกครั้ง ย้อนกลับไปที่ตัวอย่างการบันทึก ด้วยโปรแกรมตกแต่ง คุณสามารถแยกฟังก์ชันหลักได้ ในกรณีนี้คือ say_hello จากฟังก์ชันอื่นๆ ที่คุณต้องการ ในกรณีนี้คือการบันทึก
- พวกเขาปรับปรุงเฟรมเวิร์กและไลบรารี: ตัวตกแต่งถูกใช้อย่างกว้างขวางในเฟรมเวิร์กและไลบรารีของ Python เพื่อให้มีฟังก์ชันเพิ่มเติม ตัวอย่างเช่น ในเว็บเฟรมเวิร์กอย่าง Flask หรือ Django ตัวตกแต่งจะใช้สำหรับกำหนดเส้นทาง จัดการการรับรองความถูกต้อง หรือใช้มิดเดิลแวร์กับมุมมองเฉพาะ
คำสุดท้าย
มัณฑนากรมีประโยชน์อย่างเหลือเชื่อ คุณสามารถใช้มันเพื่อขยายฟังก์ชั่นโดยไม่ต้องเปลี่ยนฟังก์ชั่น สิ่งนี้มีประโยชน์เมื่อคุณต้องการจับเวลาประสิทธิภาพของฟังก์ชัน บันทึกทุกครั้งที่มีการเรียกใช้ฟังก์ชัน ตรวจสอบอาร์กิวเมนต์ก่อนเรียกใช้ฟังก์ชัน หรือตรวจสอบสิทธิ์ก่อนเรียกใช้ฟังก์ชัน เมื่อคุณเข้าใจมัณฑนากรแล้ว คุณจะสามารถเขียนโค้ดด้วยวิธีที่สะอาดขึ้นได้
ต่อไป คุณอาจต้องการอ่านบทความของเราเกี่ยวกับสิ่งอันดับและการใช้ cURL ใน Python