แม้ว่าเราจะได้ใช้คุณสมบัติเชิงวัตถุบางอย่างของไพธอนแล้ว โปรแกรมในสองบทที่ผ่านยังไม่ใช่โปรแกรมเชิงวัตถุอย่างแท้จริง เพราะว่ายังไม่มีความสัมพันธ์ระหว่างชนิดข้อมูลที่ผู้เขียนกำหนดกับฟังก์ชันที่ทำงานกับข้อมูลเหล่านั้น ขั้นถัดไปคือการแปลงฟังก์ชันดังกล่าวให้เป็นเมธอดซึ่งจะทำให้ความสัมพันธ์ชัดเจนขึ้น
ตัวอย่างโปรแกรมของบทนี้สามารถดาวน์โหลดได้ที่ http://thinkpython2.com/code/Time2.py เฉลยสำหรับแบบฝึกหัดอยู่ที่ http://thinkpython2.com/code/Point2_soln.py
ไพธอนเป็น ภาษาโปรแกรมเชิงวัตถุ (object-oriented programming language) นั่นหมายถึง ตัวภาษามีคุณสมบัติที่รองรับการโปรแกรมเชิงวัตถุ ซึ่งมีคุณลักษณะดังนี้
ตัวอย่างเช่น คลาส Time
ที่นิยามในบทที่ 16 สอดคล้องกับวิธีที่ผู้คนบันทึกเวลาของวัน และฟังก์ชันที่เราประกาศก็สอดคล้องกับสิ่งที่ผู้คนกระทำกับเวลา ในทำนองเดียวกัน คลาส Point
และ Rectangle
ใน บทที่ 15 สอดคล้องกับแนวคิดทางคณิตศาสตร์ของจุดและสี่เหลี่ยม
จนถึงตอนนี้เรายังไม่ได้ใช้ประโยชน์จากคุณสมบัติที่ไพธอนมีเพื่อสนับสนุนการเขียนโปรแกรมเชิงวัตถุ คุณสมบัติเหล่านี้ไม่ได้มีความจำเป็นอย่างเคร่งครัด และคุณสมบัติส่วนใหญ่เป็นกรณีทางเลือกทางไวยากรณ์ของสิ่งที่เราเคยทำไปแล้ว แต่ในหลายกรณีทางเลือกเหล่านั้นก็มีความรัดกุมและชัดเจนมากกว่าในการสื่อโครงสร้างของโปรแกรม
อย่างเช่นตัวอย่างใน Time1.py
ซึ่งไม่มีความสัมพันธ์ที่ชัดเจนระหว่างส่วนนิยามคลาสกับส่วนนิยามฟังก์ชันต่างๆ ที่ตามมา จากการพิเคราะห์แล้วเห็นได้ชัดว่าทุกฟังก์ชันจะรับออบเจ๊คต์ Time
อย่างน้อยหนึ่งออบเจ๊คต์เป็นอาร์กูเมนต์
ข้อสังเกตนี้กลายเป็นแรงจูงใจสำหรับ เมธอด ซึ่งก็คือฟังก์ชันที่เกี่ยวข้องอยู่กับคลาสเฉพาะคลาสใดคลาสหนึ่ง เราเคยเห็นเมธอดสำหรับ สตริง, ลิสต์, ดิกชันนารี และ ทูเพิล มาแล้ว ในบทนี้เราจะประกาศเมธอดสำหรับชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดขึ้นเอง
เมธอดมีความหมายเหมือนกับฟังก์ชัน แต่มีไวยากรณ์ที่แตกต่างกันสองประการ
ในสองสามหัวข้อถัดไป เราจะนำฟังก์ชันจากสองบทที่แล้วมาแปลงเป็นเมธอด การแปลงนี้มีขั้นตอนตรงไปตรงมาโดยสามารถทำตามลำดับขั้นตอน ถ้าคุณคุ้นเคยกับการแปลงจากรูปแบบหนึ่งไปยังอีกรูปแบบหนึ่งแล้ว คุณจะสามารถเลือกรูปแบบที่ดีที่สุดสำหรับสิ่งที่คุณกำลังทำ
ในบทที่ 16 เราได้นิยามคลาสชื่อ Time
และในหัวข้อที่ 16.1 เราได้เขียนฟังก์ชันชื่อ print_time
class Time: """Represents the time of day.""" def print_time(time): print('%.2d:%.2d:%.2d' % (time.hour, time.minute, time.second))
เราต้องส่งออบเจ๊คต์ Time
เป็นอาร์กูเมนต์เพื่อเรียกใช้ฟังก์ชัน
>>> start = Time() >>> start.hour = 9 >>> start.minute = 45 >>> start.second = 00 >>> print_time(start) 09:45:00
เพื่อสร้างเมธอด print_time
ทั้งหมดที่เราต้องทำคือ ย้ายส่วนนิยามฟังก์ชันไปไว้ในส่วนนิยามคลาส ให้ระวังการเปลี่ยนแปลงของการย่อหน้า
class Time: def print_time(time): print('%.2d:%.2d:%.2d' % (time.hour, time.minute, time.second))
ถึงตอนนี้มีสองแนวทางที่จะเรียกใช้ print_time
วีธีแรก (ไม่เป็นที่นิยม) คือ ใช้ไวยากรณ์ฟังก์ชัน
>>> Time.print_time(start) 09:45:00
ในการใช้สัญกรณ์จุดนี้ Time
เป็นชื่อของคลาส และ print_time
เป็นชื่อของเมธอด ส่วน start
ถูกส่งเป็นพารามิเตอร์
แนวทางที่สอง (กระชับมากขึ้น) เป็นการใช้ไวยากรณ์เมธอด
>>> start.print_time() 09:45:00
ในการใช้สัญกรณ์จุดนี้ print_time
เป็นชื่อของเมธอด (เช่นเคย) และ start
เป็นออบเจ๊คต์ที่เมธอดถูกเรียกใช้ซึ่งถูกเรียกว่า ประธาน (subject) เช่นเดียวกับประธานของประโยคเป็นสิ่งที่ประโยคเกี่ยวข้องด้วย ประธานของการเรียกเมธอดก็เป็นสิ่งที่เมธอดเกี่ยวข้องด้วย
ภายในเมธอด ประธานถูกกำหนดให้เป็นพารามิเตอร์แรก ดังนั้นในกรณีนี้ start
ถูกกำหนดให้กับ time
ตามธรรมเนียมแล้วพารามิเตอร์แรกของเมธอดถูกเรียกว่า self
ดังนั้นจึงนิยมที่จะเขียน print_time
เป็นดังนี้
class Time: def print_time(self): print('%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second))
เหตุผลของธรรมเนียมนี้อุปมาโดยปริยายได้ดังนี้
print_time(start)
แสดงถึงว่าฟังก์ชันเป็นผู้กระทำ เหมือนมันบอกว่า “เฮ้ print_time! นี่คือออบเจ๊คต์สำหรับให้คุณพิมพ์”start.print_time()
บ่งบอกว่า “เฮ้ start! กรุณาพิมพ์ตัวคุณเอง”การเปลี่ยนมุมมองนี้อาจจะสุภาพกว่า แต่ก็ไม่ชัดเจนว่ามีประโยชน์ ในตัวอย่างที่ผ่านมามันอาจจะไม่ชัด แต่บางครั้งการเปลี่ยนภาระจากฟังก์ชันไปเป็นออบเจ๊คต์ทำให้สามารถที่จะเขียนได้หลากหลายฟังก์ชัน (หรือเมธอด) ช่วยให้ง่ายในการบำรุงรักษาและนำไปใช้ซ้ำ
เพื่อเป็นการฝึก ให้เขียนฟังก์ชัน time_to_int
ใหม่ (จาก หัวข้อ 16.4) ให้เป็นเมธอด คุณอาจจะอดใจไม่ได้ที่จะเขียน int_to_time
เป็นเมธอด แต่นั่นก็ไม่สมเหตุสมผล เพราะว่า ไม่มีออบเจ๊คต์ที่จะเรียกใช้มัน
นี่เป็นอีกเวอร์ชันของ increment
(จากหัวข้อ 16.3) ถูกเขียนใหม่เป็นเมธอด
# inside class Time: def increment(self, seconds): seconds += self.time_to_int() return int_to_time(seconds)
เวอร์ชันนี้กำหนดให้ time_to_int
ถูกเขียนเป็นเมธอด ให้สังเกตว่าเป็นฟังก์ชันบริสุทธิ์ ไม่ใช่ตัวดัดแปลง
นี่คือวิธีเรียกใช้ increment
>>> start.print_time() 09:45:00 >>> end = start.increment(1337) >>> end.print_time() 10:07:17
ประธาน start
ถูกกำหนดให้พารามิเตอร์แรกคือ self
ส่วนอาร์กูเมนต์ 1337
ถูกกำหนดให้กับพารามิเตอร์ที่สองคือ seconds
กลไกนี้อาจทำให้เกิดความสับสน โดยเฉพาะอย่างยิ่งหากคุณทำผิดพลาด ตัวอย่างเช่น ถ้าคุณเรียกใช้ increment
ด้วยสองอาร์กูเมนต์ คุณจะได้
>>> end = start.increment(1337, 460) TypeError: increment() takes 2 positional arguments but 3 were given
แค่ข้อความแสดงข้อผิดพลาดก็เริ่มสับสน เนื่องจากมีแค่สองอาร์กูเมนต์ในวงเล็บ แต่ประธานก็ถือว่าเป็นอาร์กูเมนต์ด้วย ดังนั้นทั้งหมดจึงรวมเป็นสาม
อนึ่งอาร์กูเมนต์ตำแหน่ง (positional argument)เป็นอาร์กูเมนต์ที่ไม่มีชื่อพารามิเตอร์ นั่นคือมันไม่ใช่อาร์กูเมนต์คำสำคัญ ในการเรียกฟังก์ชันนี้
sketch(parrot, cage, dead=True)
parrot
และ cage
เป็นอาร์กูเมนต์ตำแหน่ง ส่วน dead เป็นอาร์กูเมนต์คำสำคัญ
เขียน is_after
ใหม่ (จากหัวข้อ 16.1) จะค่อนข้างซับซ้อนกว่า เนื่องจากมันรับพารามิเตอร์เป็นออบเจ๊คต์ Time สองออบเจ๊คต์ ในกรณีนี้เป็นธรรมดาที่จะตั้งชื่อพารามิเตอร์แรกเป็น self
และพารามิเตอร์ลำดับที่สองเป็นอย่างอื่น
# inside class Time: def is_after(self, other): return self.time_to_int() > other.time_to_int()
การเรียกใช้เมธอดนี้ คุณต้องเรียกมันจากออบเจ๊คต์หนึ่งและส่งออบเจ๊คต์อื่นเป็นอาร์กูเมนต์
>>> end.is_after(start) True
สิ่งหนึ่งที่ดีเกี่ยวกับไวยากรณ์นี้คือ มันเกือบอ่านเหมือนภาษาอังกฤษ: “end is after start?”
เมธอด init (ย่อมาจาก “initialization”) เป็นเมธอดพิเศษที่ถูกเรียกใช้ขณะที่ออบเจ๊คต์ถูกสร้างอินสแตนซ์ ชื่อเต็มคือ __init__
(ตัวอักษรขีดล่างสองตัวตามด้วย init
แล้วก็ตัวอักษรขีดล่างอีกสองตัว) เมธอด init สำหรับคลาส Time
อาจมีลักษณะเช่นนี้
# inside class Time: def __init__(self, hour=0, minute=0, second=0): self.hour = hour self.minute = minute self.second = second
เป็นเรื่องปกติสำหรับพารามิเตอร์ของ __init__
ที่ชื่อจะเหมือนกันกับแอตทริบิวต์ คำสั่งต่อไปนี้
self.hour = hour
เก็บค่าของพารามิเตอร์ hour
เป็นแอตทริบิวต์ของ self
พารามิเตอร์แต่ละตัวเป็นแบบทางเลือก ดังนั้นถ้าคุณเรียกใช้ Time
โดยไม่มีอาร์กูเมนต์ คุณจะได้ค่าดีฟอลท์ของพารามิเตอร์
>>> time = Time() >>> time.print_time() 00:00:00
ถ้าคุณระบุหนึ่งอาร์กูเมนต์ มันจะแทนที่ค่าของ hour
>>> time = Time (9) >>> time.print_time() 09:00:00
ถ้าคุณระบุสองอาร์กูเมนต์ มันจะแทนที่ค่าของ hour
และ minute
>>> time = Time(9, 45) >>> time.print_time() 09:45:00
และถ้าคุณระบุสามอาร์กูเมนต์ มันจะแทนที่ค่าดีฟอลท์ทั้งสามค่า
เพื่อเป็นการฝึกหัด ให้เขียนเมธอด init สำหรับคลาส Point
ที่จะรับ x
และ y
เป็นอาร์กูเมนต์แบบพารามิเตอร์ทางเลือก และกำหนดค่าให้กับแอตทริบิวต์ที่สอดคล้องกัน
เมธอด __str__
เป็นเมธอดพิเศษ เช่นเดียวกับเมธอด __init__
ซึ่งควรจะส่งคืนข้อความบรรยายออบเจ๊คต์
นี่เป็นตัวอย่างเมธอด str
สำหรับออบเจ๊คต์ Time
# inside class Time: def __str__(self): return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
เมื่อคุณพิมพ์ (print)
ออบเจ๊คต์ ไพธอนจะเรียกใช้เมธอด str
>>> time = Time(9, 45) >>> print(time) 09:45:00
เวลาผมเขียนคลาสขึ้นใหม่ ผมมักจะเริ่มต้นด้วยการเขียนเมธอด __init__
ซึ่งทำให้สร้างอินสแตนซ์ออบเจ๊คต์ได้ง่ายขึ้น และเมธอด __str__
ซึ่งเป็นประโยชน์สำหรับการแก้จุดบกพร่อง
เพื่อเป็นการฝึกหัด ให้เขียนเมธอด str
สำหรับคลาส Point
แล้วสร้างออบเจ๊คต์ Point และพิมพ์มัน
ด้วยการกำหนดเมธอดพิเศษอื่นๆ คุณสามารถระบุพฤติกรรมของตัวดำเนินการต่อชนิดข้อมูลที่กำหนดขึ้น ตัวอย่างเช่น ถ้าคุณนิยามเมธอดชื่อ __add__
สำหรับคลาส Time
คุณสามารถใช้ตัวดำเนินการ +
กับออบเจ๊คต์ Time
การกำหนดมีลักษณะคล้ายอย่างนี้
# inside class Time: def __add__(self, other): seconds = self.time_to_int() + other.time_to_int() return int_to_time(seconds)
และคุณสามารถเรียกใช้ได้ดังนี้
>>> start = Time(9, 45) >>> duration = Time(1, 35) >>> print(start + duration) 11:20:00
เมื่อคุณใช้ตัวดำเนินการ +
กับออบเจ๊คต์ Time ไพธอนเรียกใช้ เมธอด __add__
เมื่อคุณพิมพ์ผลลัพธ์ ไพธอนเรียกใช้เมธอด __str__
จึงมีอะไรเกิดขึ้นมากมายอยู่เบื้องหลัง
การเปลี่ยนพฤติกรรมของตัวดำเนินการซึ่งจะใช้ได้กับชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดขึ้นนี้เรียกว่า การโอเวอร์โหลดตัวดำเนินการ (operator overloading) และทุกตัวดำเนินการในไพธอนจะมีเมธอดพิเศษคล้ายกับ __add__
ดูรายละเอียดเพิ่มเติมได้ที่ http://docs.python.org/3/reference/datamodel.html#specialnames
เพื่อเป็นการฝึกหัด ให้เขียนเมธอด add
สำหรับคลาส Point
ในส่วนก่อนหน้านี้เราได้บวกสองออบเจ๊คต์ Time แต่คุณอาจต้องการบวกจำนวนเต็มให้กับออบเจ๊คต์ Time ต่อไปนี้เป็นเวอร์ชันของ __add__
ที่ตรวจสอบ ชนิดข้อมูลของ other แล้วเรียกใช้งาน add_time
หรือ increment
# inside class Time: def __add__(self, other): if isinstance(other, Time): return self.add_time(other) else: return self.increment(other) def add_time(self, other): seconds = self.time_to_int() + other.time_to_int() return int_to_time(seconds) def increment(self, seconds): seconds += self.time_to_int() return int_to_time(seconds)
ฟังก์ชันในตัว isinstance
รับค่าและคลาสออบเจ๊คต์และส่งกลับค่า True
หากค่านั้นเป็นอินสแตนซ์ของคลาส
ถ้า other
เป็นออบเจ๊คต์ Time เมธอด __add__
จะเรียกใช้ add_time
มิฉะนั้นจะถือว่าพารามิเตอร์เป็นตัวเลขและเรียกใช้เมธอด increment
การดำเนินการนี้เรียกว่า การจัดการตามชนิดข้อมูล (type-based dispatch) เพราะมันส่งการคำนวณไปยังเมธอดต่างๆ ขึ้นอยู่กับประเภทของอาร์กูเมนต์
นี่คือตัวอย่างที่ใช้ตัวดำเนินการ +
กับชนิดข้อมูลประเภทต่างๆ
>>> start = Time(9, 45) >>> duration = Time(1, 35) >>> print(start + duration) 11:20:00 >>> print(start + 1337) 10:07:17
น่าเสียดายที่การใช้งานการบวกนี้ไม่อาจสับสลับลำดับ ถ้าจำนวนเต็มเป็นพารามิเตอร์แรกคุณจะได้
>>> print(1337 + start) TypeError: unsupported operand type(s) for +: 'int' and 'instance'
ปัญหาคือ แทนที่จะขอให้ออบเจ๊คต์ Time บวกด้วยจำนวนเต็ม ไพธอนกลับขอให้บวกจำนวนเต็มด้วยออบเจ๊คต์ Time ทำให้ไม่รู้ว่าจะทำอย่างไร แต่มีวิธีแก้ปัญหาที่ชาญฉลาดสำหรับปัญหานี้ เมธอดพิเศษ __radd__
ซึ่งย่อมาจาก “right-side add” เมธอดนี้ถูกเรียกใช้เมื่อออบเจ๊คต์ Time ปรากฏอยู่ด้านขวาของตัวดำเนินการ +
นี่เป็นตัวอย่างการประกาศ
# inside class Time: def __radd__(self, other): return self.__add__(other)
และนี่คือวิธีการใช้
>>> print(1337 + start) 10:07:17
เพื่อเป็นการฝึก ให้เขียนเมธอด add
สำหรับคลาส Point ที่สามารถใช้ได้กับทั้งออบเจ๊คต์ Point และทูเพิล
การจัดการตามชนิดข้อมูลจะมีประโยชน์เมื่อจำเป็น แต่ (โชคดี) ที่ไม่จำเป็นเสมอไป บ่อยครั้งที่คุณสามารถหลีกเลี่ยงได้โดยการเขียนฟังก์ชันที่ทำงานได้อย่างถูกต้องสำหรับอาร์กูเมนต์ต่างชนิดข้อมูลกัน
ฟังก์ชันหลายอย่างที่เราเขียนสำหรับสายอักขระสามารถใช้ได้กับข้อมูลลำดับประเภทอื่นๆ ตัวอย่างเช่นในหัวข้อ 11.2 เราใช้ histogram
เพื่อนับจำนวนครั้งที่ตัวอักษรแต่ละตัวปรากฏในคำ
def histogram(s): d = dict() for c in s: if c not in d: d[c] = 1 else: d[c] = d[c]+1 return d
ฟังก์ชันนี้ยังใช้ได้กับลิสต์ ทูเพิล และแม้แต่ดิกชันนารีด้วย ตราบใดที่องค์ประกอบของ s
สามารถแฮชได้ ซึ่งจะสามารถใช้เป็นกุญแจ (key) ใน d
ได้
>>> t = ['spam', 'egg', 'spam', 'spam', 'bacon', 'spam'] >>> histogram(t) {'bacon': 1, 'egg': 1, 'spam': 4}
ฟังก์ชันที่ทำงานได้กับหลายชนิดข้อมูลเรียกว่า มีพหุสัณฐาน (polymorphic) ภาวะพหุสัณฐานสามารถอำนวยความสะดวกในการใช้รหัสซ้ำได้ ตัวอย่างเช่น ฟังก์ชันในตัว sum ซึ่งหาผลรวมของสมาชิกในลำดับ ทำงานได้เสมอตราบเท่าที่องค์ประกอบของลำดับสนับสนุนการบวก
เนื่องจากออบเจ๊คต์ Time มีเมธอด add
จึงใช้งานได้กับ sum
>>> t1 = Time(7, 43) >>> t2 = Time(7, 41) >>> t3 = Time(7, 37) >>> total = sum([t1, t2, t3]) >>> print(total) 23:01:00
โดยทั่วไป ถ้าการดำเนินการทั้งหมดภายในฟังก์ชันใช้งานได้กับชนิดข้อมูลที่กำหนด ฟังก์ชันนั้นจะใช้งานได้กับชนิดข้อมูลนั้น
ภาวะพหุสัณฐานที่ดีที่สุดคือชนิดที่ไม่ได้ตั้งใจ ซึ่งคุณจะค้นพบว่าฟังก์ชันที่คุณเขียนไปแล้วสามารถนำไปใช้กับชนิดข้อมูลที่คุณไม่เคยวางแผนไว้ได้
การเพิ่มแอตทริบิวต์ให้กับออบเจ๊คต์ ณ จุดใดๆ ในการทำงานของโปรแกรมนั้นสามารถทำได้ แต่ถ้าคุณมีออบเจ๊คต์ประเภทเดียวกันที่มีแอตทริบิวต์ไม่เหมือนกันจะทำให้เกิดข้อผิดพลาดได้ง่าย ถือเป็นความคิดที่ดีที่จะกำหนดค่าตั้งต้นแอตทริบิวต์ทั้งหมดของออบเจ๊คต์ในเมธอด init
หากคุณไม่แน่ใจว่าออบเจ๊คต์มีแอตทริบิวต์อย่างใดอย่างหนึ่งหรือไม่ คุณสามารถตรวจสอบด้วยฟังก์ชันในตัว hasattr
(ดูหัวข้อ 15.7)
อีกวิธีหนึ่งในการเข้าถึงแอตทริบิวต์คือใช้ฟังก์ชันในตัว vars
ซึ่งรับออบเจ๊คต์และส่งคืนดิกชันนารีที่แม็พชื่อแอ็ตทริบิวต์ (เป็นสตริง) กับค่าของมัน
>>> p = Point(3, 4) >>> vars(p) {'y': 4, 'x': 3}
เพื่อจุดประสงค์ในการดีบัก คุณอาจพบว่ามีประโยชน์ในการพกพาฟังก์ชันนี้ไว้ใช้งาน
def print_attributes(obj): for attr in vars(obj): print(attr, getattr(obj, attr))
ฟังก์ชัน print_attributes
สำรวจดิกชันนารีและพิมพ์ชื่อแอตทริบิวต์แต่ละรายการกับค่าของมัน
The built-in function getattr
takes an object and an attribute name (as a string) and returns the attribute’s value. ฟังก์ชันในตัว getattr
รับออบเจ๊คต์และชื่อแอ็ตทริบิวต์ (เป็นสตริง) และจะส่งคืนค่าของแอ็ตทริบิวต์นั้น
เป้าหมายหนึ่งของการออกแบบเชิงวัตถุคือการทำให้ซอฟต์แวร์สามารถบำรุงรักษาได้มากขึ้น ซึ่งหมายความว่าคุณสามารถให้โปรแกรมทำงานต่อไปได้แม้ส่วนอื่นๆ ของระบบเปลี่ยนไปและสามารถปรับเปลี่ยนโปรแกรมให้ตรงตามข้อกำหนดใหม่ได้
หลักการออกแบบที่ช่วยให้บรรลุเป้าหมายนั้นคือการแยกส่วนต่อประสานออกจากการนำไปใช้งาน นั่นหมายถึงเมธอดที่คลาสมีไม่ควรขึ้นอยู่กับแอตทริบิวต์ที่แสดงออกมา
ตัวอย่างเช่น ในบทนี้เราได้พัฒนาคลาสที่แสดงถึงช่วงเวลาของวัน เมธอดที่คลาสนี้มีให้ ได้แก่ time_to_int
, is_after
และ add_time
รายละเอียดของการพัฒนาขึ้นอยู่กับวิธีที่เราแทนค่าเวลา ในบทนี้แอตทริบิวต์ของออบเจ๊คต์ Time
ได้แก่ hour
minute
และ second
อีกทางเลือกหนึ่งคือ เราสามารถแทนที่แอตทริบิวต์เหล่านี้ด้วยจำนวนเต็มเดียวที่แทนจำนวนวินาทีตั้งแต่เที่ยงคืน การทำเช่นนี้จะทำให้วิธีการบางอย่าง เช่น is_after
เขียนง่ายขึ้น แต่มันทำให้วิธีการอื่นๆ ยากขึ้น
หลังจากที่คุณปรับใช้คลาสใหม่ คุณอาจค้นพบการพัฒนาที่ดีขึ้น หากส่วนอื่นๆ ของโปรแกรมกำลังใช้คลาสของคุณ มันอาจใช้เวลานานและเกิดข้อผิดพลาดในการเปลี่ยนอินเทอร์เฟซ
แต่ถ้าคุณออกแบบอินเทอร์เฟซอย่างระมัดระวัง คุณสามารถเปลี่ยนการทำงานโดยไม่ต้องเปลี่ยนอินเทอร์เฟซ ซึ่งหมายความว่าส่วนอื่นๆ ของโปรแกรมไม่จำเป็นต้องเปลี่ยน
+
เพื่อให้ทำงานได้กับชนิดข้อมูลที่กำหนดโดยโปรแกรมเมอร์
แบบฝึกหัด 1
ดาวน์โหลดโค้ดของบทนี้จาก http://thinkpython2.com/code/Time2.py เปลี่ยนแอตทริบิวต์ของ Time
ให้เป็นจำนวนเต็มเดียวที่แทนวินาทีตั้งแต่เที่ยงคืน จากนั้นแก้ไขเมธอด (และฟังก์ชัน int_to_time
) เพื่อให้ทำงานได้กับการพัฒนาใหม่ คุณไม่ควรแก้ไขโค้ดทดสอบใน main
หลังจากทำเสร็จแล้ว ผลลัพธ์ที่ได้ควรจะเหมือนกับผลลัพธ์ก่อนหน้านี้ เฉลย: http://thinkpython2.com/code/Time2_soln.py
แบบฝึกหัด 2
แบบฝึกหัดนี้เป็นอุทาหรณ์เกี่ยวกับเรื่องที่พบบ่อยที่สุดเรื่องหนึ่งและหาข้อผิดพลาดได้ยากของไพธอน เขียนนิยามความของคลาสชื่อ Kangaroo
พร้อมกับเมธอดต่อไปนี้
__init__
เมธอดที่กำหนดค่าตั้งต้นให้กับแอตทริบิวต์ ชื่อ pouch_contents
เป็นลิสต์ว่างput_in_pouch
เมธอดที่รับออบเจ๊คต์ทุกชนิดและเพิ่มเข้าไปใน pouch_contents
__str__
ที่ส่งคืนค่าสตริงอธิบายออบเจ๊คต์ Kangaroo และรายละเอียดในกระเป๋า
ทดสอบโค้ดของคุณโดยสร้างออบเจ๊คต์ Kangaroo
สองอัน และกำหนดให้กับตัวแปรที่ชื่อ kanga
และ roo
จากนั้นเพิ่ม roo
ลงในกระเป๋าของ kanga
ดาวน์โหลด http://thinkpython2.com/code/BadKangaroo.py ซึ่งมีวิธีแก้ปัญหาก่อนหน้านี้ด้วยจุดบกพร่องขนาดใหญ่ที่น่ารังเกียจตัวหนึ่ง ค้นหาและแก้ไขจุดบกพร่องนั้น
ถ้าติดขัดคุณสามารถดาวน์โหลด http://thinkpython2.com/code/GoodKangaroo.py ซึ่งอธิบายปัญหาและสาธิตวิธีแก้ไข
https://greenteapress.com/thinkpython2/html/thinkpython2018.html