Table of Contents

17. คลาสและเมธอด

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

ตัวอย่างโปรแกรมของบทนี้สามารถดาวน์โหลดได้ที่ http://thinkpython2.com/code/Time2.py เฉลยสำหรับแบบฝึกหัดอยู่ที่ http://thinkpython2.com/code/Point2_soln.py

17.1 คุณสมบัติเชิงวัตถุ

ไพธอนเป็น ภาษาโปรแกรมเชิงวัตถุ (object-oriented programming language) นั่นหมายถึง ตัวภาษามีคุณสมบัติที่รองรับการโปรแกรมเชิงวัตถุ ซึ่งมีคุณลักษณะดังนี้

ตัวอย่างเช่น คลาส Time ที่นิยามในบทที่ 16 สอดคล้องกับวิธีที่ผู้คนบันทึกเวลาของวัน และฟังก์ชันที่เราประกาศก็สอดคล้องกับสิ่งที่ผู้คนกระทำกับเวลา ในทำนองเดียวกัน คลาส Point และ Rectangle ใน บทที่ 15 สอดคล้องกับแนวคิดทางคณิตศาสตร์ของจุดและสี่เหลี่ยม

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

อย่างเช่นตัวอย่างใน Time1.py ซึ่งไม่มีความสัมพันธ์ที่ชัดเจนระหว่างส่วนนิยามคลาสกับส่วนนิยามฟังก์ชันต่างๆ ที่ตามมา จากการพิเคราะห์แล้วเห็นได้ชัดว่าทุกฟังก์ชันจะรับออบเจ๊คต์ Time อย่างน้อยหนึ่งออบเจ๊คต์เป็นอาร์กูเมนต์

ข้อสังเกตนี้กลายเป็นแรงจูงใจสำหรับ เมธอด ซึ่งก็คือฟังก์ชันที่เกี่ยวข้องอยู่กับคลาสเฉพาะคลาสใดคลาสหนึ่ง เราเคยเห็นเมธอดสำหรับ สตริง, ลิสต์, ดิกชันนารี และ ทูเพิล มาแล้ว ในบทนี้เราจะประกาศเมธอดสำหรับชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดขึ้นเอง

เมธอดมีความหมายเหมือนกับฟังก์ชัน แต่มีไวยากรณ์ที่แตกต่างกันสองประการ

ในสองสามหัวข้อถัดไป เราจะนำฟังก์ชันจากสองบทที่แล้วมาแปลงเป็นเมธอด การแปลงนี้มีขั้นตอนตรงไปตรงมาโดยสามารถทำตามลำดับขั้นตอน ถ้าคุณคุ้นเคยกับการแปลงจากรูปแบบหนึ่งไปยังอีกรูปแบบหนึ่งแล้ว คุณจะสามารถเลือกรูปแบบที่ดีที่สุดสำหรับสิ่งที่คุณกำลังทำ

17.2 การพิมพ์ออบเจ๊คต์

ในบทที่ 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))

เหตุผลของธรรมเนียมนี้อุปมาโดยปริยายได้ดังนี้

การเปลี่ยนมุมมองนี้อาจจะสุภาพกว่า แต่ก็ไม่ชัดเจนว่ามีประโยชน์ ในตัวอย่างที่ผ่านมามันอาจจะไม่ชัด แต่บางครั้งการเปลี่ยนภาระจากฟังก์ชันไปเป็นออบเจ๊คต์ทำให้สามารถที่จะเขียนได้หลากหลายฟังก์ชัน (หรือเมธอด) ช่วยให้ง่ายในการบำรุงรักษาและนำไปใช้ซ้ำ

เพื่อเป็นการฝึก ให้เขียนฟังก์ชัน time_to_int ใหม่ (จาก หัวข้อ 16.4) ให้เป็นเมธอด คุณอาจจะอดใจไม่ได้ที่จะเขียน int_to_time เป็นเมธอด แต่นั่นก็ไม่สมเหตุสมผล เพราะว่า ไม่มีออบเจ๊คต์ที่จะเรียกใช้มัน

17.3 อีกตัวอย่าง

นี่เป็นอีกเวอร์ชันของ 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 เป็นอาร์กูเมนต์คำสำคัญ

17.4 ตัวอย่างที่ซับซ้อนมากขึ้น

เขียน 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?”

17.5 เมธอด init

เมธอด 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 เป็นอาร์กูเมนต์แบบพารามิเตอร์ทางเลือก และกำหนดค่าให้กับแอตทริบิวต์ที่สอดคล้องกัน

17.6 เมธอด __str__

เมธอด __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 และพิมพ์มัน

17.7 การโอเวอร์โหลดตัวดำเนินการ

ด้วยการกำหนดเมธอดพิเศษอื่นๆ คุณสามารถระบุพฤติกรรมของตัวดำเนินการต่อชนิดข้อมูลที่กำหนดขึ้น ตัวอย่างเช่น ถ้าคุณนิยามเมธอดชื่อ __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

17.8 การจัดการตามชนิดข้อมูล

ในส่วนก่อนหน้านี้เราได้บวกสองออบเจ๊คต์ 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 และทูเพิล

17.9 ภาวะพหุสัณฐาน

การจัดการตามชนิดข้อมูลจะมีประโยชน์เมื่อจำเป็น แต่ (โชคดี) ที่ไม่จำเป็นเสมอไป บ่อยครั้งที่คุณสามารถหลีกเลี่ยงได้โดยการเขียนฟังก์ชันที่ทำงานได้อย่างถูกต้องสำหรับอาร์กูเมนต์ต่างชนิดข้อมูลกัน

ฟังก์ชันหลายอย่างที่เราเขียนสำหรับสายอักขระสามารถใช้ได้กับข้อมูลลำดับประเภทอื่นๆ ตัวอย่างเช่นในหัวข้อ 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

โดยทั่วไป ถ้าการดำเนินการทั้งหมดภายในฟังก์ชันใช้งานได้กับชนิดข้อมูลที่กำหนด ฟังก์ชันนั้นจะใช้งานได้กับชนิดข้อมูลนั้น

ภาวะพหุสัณฐานที่ดีที่สุดคือชนิดที่ไม่ได้ตั้งใจ ซึ่งคุณจะค้นพบว่าฟังก์ชันที่คุณเขียนไปแล้วสามารถนำไปใช้กับชนิดข้อมูลที่คุณไม่เคยวางแผนไว้ได้

17.10 การดีบัก

การเพิ่มแอตทริบิวต์ให้กับออบเจ๊คต์ ณ จุดใดๆ ในการทำงานของโปรแกรมนั้นสามารถทำได้ แต่ถ้าคุณมีออบเจ๊คต์ประเภทเดียวกันที่มีแอตทริบิวต์ไม่เหมือนกันจะทำให้เกิดข้อผิดพลาดได้ง่าย ถือเป็นความคิดที่ดีที่จะกำหนดค่าตั้งต้นแอตทริบิวต์ทั้งหมดของออบเจ๊คต์ในเมธอด 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 รับออบเจ๊คต์และชื่อแอ็ตทริบิวต์ (เป็นสตริง) และจะส่งคืนค่าของแอ็ตทริบิวต์นั้น

17.11 ส่วนต่อประสานและการพัฒนา

เป้าหมายหนึ่งของการออกแบบเชิงวัตถุคือการทำให้ซอฟต์แวร์สามารถบำรุงรักษาได้มากขึ้น ซึ่งหมายความว่าคุณสามารถให้โปรแกรมทำงานต่อไปได้แม้ส่วนอื่นๆ ของระบบเปลี่ยนไปและสามารถปรับเปลี่ยนโปรแกรมให้ตรงตามข้อกำหนดใหม่ได้

หลักการออกแบบที่ช่วยให้บรรลุเป้าหมายนั้นคือการแยกส่วนต่อประสานออกจากการนำไปใช้งาน นั่นหมายถึงเมธอดที่คลาสมีไม่ควรขึ้นอยู่กับแอตทริบิวต์ที่แสดงออกมา

ตัวอย่างเช่น ในบทนี้เราได้พัฒนาคลาสที่แสดงถึงช่วงเวลาของวัน เมธอดที่คลาสนี้มีให้ ได้แก่ time_to_int, is_after และ add_time

รายละเอียดของการพัฒนาขึ้นอยู่กับวิธีที่เราแทนค่าเวลา ในบทนี้แอตทริบิวต์ของออบเจ๊คต์ Time ได้แก่ hour minute และ second

อีกทางเลือกหนึ่งคือ เราสามารถแทนที่แอตทริบิวต์เหล่านี้ด้วยจำนวนเต็มเดียวที่แทนจำนวนวินาทีตั้งแต่เที่ยงคืน การทำเช่นนี้จะทำให้วิธีการบางอย่าง เช่น is_after เขียนง่ายขึ้น แต่มันทำให้วิธีการอื่นๆ ยากขึ้น

หลังจากที่คุณปรับใช้คลาสใหม่ คุณอาจค้นพบการพัฒนาที่ดีขึ้น หากส่วนอื่นๆ ของโปรแกรมกำลังใช้คลาสของคุณ มันอาจใช้เวลานานและเกิดข้อผิดพลาดในการเปลี่ยนอินเทอร์เฟซ

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

17.12 อภิธานศัพท์

17.13 แบบฝึกหัด

แบบฝึกหัด 1
ดาวน์โหลดโค้ดของบทนี้จาก http://thinkpython2.com/code/Time2.py เปลี่ยนแอตทริบิวต์ของ Time ให้เป็นจำนวนเต็มเดียวที่แทนวินาทีตั้งแต่เที่ยงคืน จากนั้นแก้ไขเมธอด (และฟังก์ชัน int_to_time) เพื่อให้ทำงานได้กับการพัฒนาใหม่ คุณไม่ควรแก้ไขโค้ดทดสอบใน main หลังจากทำเสร็จแล้ว ผลลัพธ์ที่ได้ควรจะเหมือนกับผลลัพธ์ก่อนหน้านี้ เฉลย: http://thinkpython2.com/code/Time2_soln.py

แบบฝึกหัด 2
แบบฝึกหัดนี้เป็นอุทาหรณ์เกี่ยวกับเรื่องที่พบบ่อยที่สุดเรื่องหนึ่งและหาข้อผิดพลาดได้ยากของไพธอน เขียนนิยามความของคลาสชื่อ Kangaroo พร้อมกับเมธอดต่อไปนี้

  1. __init__ เมธอดที่กำหนดค่าตั้งต้นให้กับแอตทริบิวต์ ชื่อ pouch_contents เป็นลิสต์ว่าง
  2. put_in_pouch เมธอดที่รับออบเจ๊คต์ทุกชนิดและเพิ่มเข้าไปใน pouch_contents
  3. เมธอด __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