User Tools

Site Tools


python:class_objects

15. คลาสและออบเจ๊คต์

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

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

15.1 ชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดเอง

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

สัญลักษณ์สำหรับแทนจุดในทางคณิตศาสตร์มักเขียนคู่ลำดับไว้ในวงเล็บและคั่นตัวเลขด้วยจุลภาค ตัวอย่างเช่น $(0,0)$ แทนตำแหน่งต้นกำเนิด และ $(x,y)$ ใช้แทนจุดที่ห่างไปทางขวา $x$ หน่วย และสูงขึ้นไป $y$ หน่วยจากจุดต้นกำเนิด

มีหลากหลายแนวทางที่อาจใช้แทนจุดในภาษาไพธอน

  • เราสามารถเก็บค่าคู่ลำดับแยกกันในสองตัวแปร x และ y
  • เราสามารถเก็บค่าคู่ลำดับเป็นสมาชิกในลิสต์หรือในทูเพิล
  • เราสามารถสร้างชนิดข้อมูลใหม่เพื่อใช้แทนจุดเป็นออบเจ๊คต์ (object)

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

ชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดขึ้นนี้มีชื่อเรียกอีกอย่างว่า คลาส (class) การนิยามคลาสมีลักษณะดังนี้

class Point:
    """Represents a point in 2-D space."""

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

การนิยามคลาสชื่อ Point จะสร้าง คลาสออบเจ๊คต์ (class object) ขึ้น

>>> Point
<class '__main__.Point'>

เนื่องจาก Point ถูกนิยามในระดับบนสุด ดังนั้นจึงมี “ชื่อเต็ม” ว่า __main__.Point

คลาสออบเจ๊คต์เป็นเหมือนโรงงานสำหรับสร้างออบเจ๊คต์ เราเรียกใช้ Point เหมือนกับการเรียกฟังก์ชันเพื่อสร้างออบเจ๊คต์ Point

>>> blank = Point()
>>> blank
<__main__.Point object at 0xb7e9d3ac>

ค่าที่ถูกส่งออกมาเป็นอ้างอิงไปยังออบเจ๊คต์ของ Point ซึ่งเรากำหนดค่าให้กับ blank

การสร้างออบเจ๊คต์ใหม่ขึ้นมานี้จะเรียกว่า การสร้างอินสแตนซ์ (instantiation) และออบเจ๊คต์ที่ได้คือหนึ่ง อินสแตนซ์ (instance) ของคลาสนั้น

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

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

15.2 แอตทริบิวต์

เราสามารถกำหนดค่าให้กับอินสแตนซ์โดยการใช้สัญกรณ์จุด

>>> blank.x = 3.0
>>> blank.y = 4.0

ไวยากรณ์เช่นนี้เหมือนกับไวยากรณ์ที่ใช้ในการเลือกตัวแปรจากโมดูล ตัวอย่างเช่น math.pi หรือ string.whitespace ในกรณีนี้เราได้กำหนดค่าให้กับสมาชิกของออบเจ๊คต์ แต่อย่างไรก็ตามสมาชิกเหล่านี้ถูกเรียกว่า แอตทริบิวต์ (attributes)

ออกเสียงเหมือนคำนาม “AT-trib-ute” เน้นเสียงของพยางค์แรก ตรงกันข้ามกับคำกริยา “a-TRIB-ute” ซึ่งจะเน้นเสียงของพยางค์ที่สอง

แผนภาพต่อไปนี้แสดงผลลัพธ์ของการกำหนดค่าเหล่านี้ แผนภาพสถานะที่แสดงออบเจ๊คต์และแอตทริบิวต์ของออบเจ๊คต์จะถูกเรียกว่า แผนภาพออบเจ๊คต์ (object diagram) ดูรูปที่ 15.1

 แผนภาพออบเจ๊คต์
รูปที่ 15.1 แผนภาพออบเจ๊คต์

ตัวแปร blank อ้างถึงออบเจ๊คต์ Point ซึ่งบรรจุสองแอตทริบิวต์ แต่ละแอตทริบิวต์อ้างอิงถึงตัวเลขทศนิยมอย่างละตัว

เราสามารถอ่านค่าของแอตทริบิวต์แต่ละตัวด้วยวิธีการเดียวกัน

>>> blank.y
4.0
>>> x = blank.x
>>> x
3.0

นิพจน์ blank.x หมายถึง “ไปยังตำแหน่งที่อ้างถึงโดยออบเจ๊คต์ blank และอ่านค่าของแอตทริบิวต์ x” ในตัวอย่างจะเห็นว่า มีการกำหนดค่าให้กับตัวแปร x ซึ่งไม่ทำให้เกิดการขัดแย้งระหว่างตัวแปร x และแอตทริบิวต์ x แต่อย่างใด

คุณสามารถใช้สัญกรณ์จุดเป็นส่วนหนึ่งของนิพจน์ใดๆ ได้ ตัวอย่างเช่น

>>> '(%g, %g)' % (blank.x, blank.y)
'(3.0, 4.0)'
>>> distance = math.sqrt(blank.x**2 + blank.y**2)
>>> distance
5.0

เราสามารถส่งผ่านอินสแตนซ์เป็นอาร์กูเมนต์ได้ตามปกติ ตัวอย่างเช่น

def print_point(p):
    print('(%g, %g)' % (p.x, p.y))

print_point รับจุดหนึ่งจุดเข้ามาเป็นอาร์กูเมนต์แล้วแสดงค่าแบบสัญลักษณ์ทางคณิตศาสตร์ จึงสามารถเรียกใช้โดยส่งผ่าน blank เป็นอาร์กูเมนต์ได้

>>> print_point(blank)
(3.0, 4.0)

ภายในฟังก์ชัน p เป็นสมนามของ blank ดังนั้นถ้าฟังก์ชันมีการแก้ไขค่าของ p ก็จะส่งผลต่อ blank เช่นกัน

เพื่อเป็นการฝึก ให้เขียนฟังก์ชันชื่อ distance_between_points ซึ่งรับจุดสองจุดเป็นอาร์กูเมนต์และให้ค่าออกมาเป็นระยะห่างระหว่างสองจุดนั้น

15.3 รูปสี่เหลี่ยม

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

มีอย่างน้อยสองแนวทางที่เป็นไปได้

  • เราสามารถระบุมุมใดมุมหนึ่ง (หรือจุดศูนย์กลาง) ความกว้าง และความยาว
  • เราสามารถระบุคู่จุดมุมตรงข้าม

ณ จุดนี้ ยังเป็นการยากที่จะบอกว่าวิธีการไหนจะเป็นวิธีการที่ดีกว่าวิธีอื่น ดังนั้นเราจะลองทำตามวิธีแรกเพื่อเป็นตัวอย่าง

นี่เป็นนิยามของคลาสดังกล่าว

class Rectangle:
    """Represents a rectangle. 
 
    attributes: width, height, corner.
    """

ด็อกสตริงได้ระบุรายการแอตทริบิวต์ไว้ด้วยคือ width และ height เป็นตัวเลข ส่วน corner เป็นออบเจ๊คต์ Point ที่ใช้ระบุตำแหน่งของมุมซ้ายล่าง

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

box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0

นิพจน์ box.corner.x หมายถึง “ไปยังตำแหน่งที่ออบเจ๊คต์ box อ้างถึงและเลือกแอตทริบิวต์ชื่อว่า corner ซึ่งจะเป็นการไปยังออบเจ๊คต์นั้นและเลือกแอตทริบิวต์ชื่อ x

 แผนภาพออบเจ๊คต์
รูปที่ 16.2 แผนภาพออบเจ๊คต์

รูปที่ 16.2 แสดงสถานะของออบเจ๊คต์ที่ได้ ออบเจ๊คต์ที่ถูกกำหนดเป็นแอตทริบิวต์ของออบเจ๊คต์อื่นจะ ฝังตัว อยู่ภายในอีกที

15.4 อิสแสตนซ์เป็นค่าคืน

ฟังก์ชันสามารถส่งค่าออกมาเป็นอินสแตนซ์ได้ ตัวอย่างเช่น ฟังก์ชัน find_center รับอาร์กูเมนต์เป็นออบเจ๊คต์ Rectangle แล้วให้ค่าออกมาเป็นอินสแตนซ์ของ Point ที่มีพิกัดตำแหน่งกึ่งกลางของรูปสี่เหลี่ยม

def find_center(rect):
    p = Point()
    p.x = rect.corner.x + rect.width/2
    p.y = rect.corner.y + rect.height/2
    return p

นี่เป็นตัวอย่างการส่ง box เป็นอาร์กูเมนต์และกำหนดค่าผลลัพธ์ซึ่งเป็นอินสแตนซ์ Point ให้กับ center

>>> center = find_center(box)
>>> print_point(center)
(50, 100)

15.5 ออบเจ๊คต์เป็นชนิดข้อมูลที่เปลี่ยนแปลงได้

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

box.width = box.width + 50
box.height = box.height + 100

เราสามารถเขียนฟังก์ชันเพื่อแก้ไขออบเจ๊คต์ ตัวอย่างเช่น ฟังก์ชัน grow_rectangle ซึ่งรับออบเจ๊คต์ Rectangle และตัวเลขอีกสองตัวคือ dwidth และ dheight สำหรับใช้บวกเพิ่มให้กับความกว้างและความสูงของรูปสี่เหลี่ยม

def grow_rectangle(rect, dwidth, dheight):
    rect.width += dwidth
    rect.height += dheight

นี่คือตัวอย่างที่แสดงให้เห็นถึงผลที่ได้

>>> box.width, box.height
(150.0, 300.0)
>>> grow_rectangle(box, 50, 100)
>>> box.width, box.height
(200.0, 400.0)

rect ที่อยู่ภายในฟังก์ชันเป็นสมนามของ box ดังนั้นเมื่อใดที่ฟังก์ชันมีการแก้ไขค่าของ rect ค่าของ box ก็จะเปลี่ยนตาม

เพื่อเป็นการฝึก ให้เขียนฟังก์ชันชื่อว่า move_rectangle ซึ่งรับอาร์กูเมนต์เป็นออบเจ๊คต์ Rectangle และตัวเลขอีกสองจำนวนคือ dx และ dy ให้ฟังก์ชันทำการย้ายตำแหน่งของสี่เหลี่ยมด้วยบวกเพิ่ม dx ให้กับพิกัด x ของ corner และบวกเพิ่ม dy ให้กับพิกัด y ของ corner

15.6 การทำสำเนา

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

การทำสำเนาออบเจ๊คต์เป็นอีกทางเลือกแทนการทำสมนาม โมดูล copy มีฟังก์ชันชื่อว่า copy ซึ่งสามารถสร้างสำเนาของออบเจ๊คต์ใดก็ได้

>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
 
>>> import copy
>>> p2 = copy.copy(p1)

p1 และ p2 มีข้อมูลบรรจุภายในเหมือนกัน แต่เป็นคนละออบเจ๊คต์

>>> print_point(p1)
(3, 4)
>>> print_point(p2)
(3, 4)
>>> p1 is p2
False
>>> p1 == p2
False

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

ถ้าเราใช้ copy.copy เพื่อสำเนาออบเจ๊คต์ Rectangle เราจะพบว่ามีการสำเนาเฉพาะ Rectangle แต่จะไม่สำเนาออบเจ๊คต์ฝังตัว Point

>>> box2 = copy.copy(box)
>>> box2 is box
False
>>> box2.corner is box.corner
True

 แผนภาพออบเจ๊คต์
รูปที่ 16.3 แผนภาพออบเจ๊คต์

รูปที่ 16.3 แสดงแผนภาพออบเจ๊คต์ให้เห็นว่ามีลักษณะอย่างไร การทำงานเช่นนี้เรียกได้ว่าเป็น การสำเนาตื้น (shallow copy) เนื่องจากทำการสำเนาเฉพาะตัวออบเจ๊คต์และทุกการอ้างอิง แต่ไม่สำเนาออบเจ๊คต์ที่ฝังอยู่ในออบเจ๊คต์นั้น

สำหรับแอปพลิเคชันส่วนใหญ่ไม่ได้ต้องการผลลัพธ์เช่นนี้ ในตัวอย่างนี้การเรียกใช้ grow_rectangle กับออบเจ๊คต์ Rectangle ใดจะไม่สร้างผลกระทบต่อออบเจ๊คต์อื่น แต่ถ้าเรียกใช้ move_rectangle จะส่งผลกระทบทั้งคู่ พฤติกรรมเช่นนี้สร้างความสับสนและมีข้อผิดพลาดง่าย

โชคดีที่โมดูล copy ได้เตรียมเมธอดชื่อ deepcopy ซึ่งคัดลอกไม่เฉพาะออบเจ๊คต์ที่ระบุแต่ยังคัดลอกรวมไปถึงออบเจ๊คต์ที่ถูกอ้างถึงและออบเจ๊คต์อื่นที่ถูกอ้างถึงด้วย ออบเจ๊คต์เหล่านั้น ไปเรื่อยๆ จึงไม่น่าแปลกใจที่กระบวนการนี้เรียกว่า การสำเนาลึก (deep copy)

>>> box3 = copy.deepcopy(box)
>>> box3 is box
False
>>> box3.corner is box.corner
False

box3 และ box เป็นคนละออบเจ๊คต์ที่แยกจากกันอย่างสมบูรณ์

เพื่อเป็นการฝึก ให้เขียนฟังก์ชัน move_rectangle อีกเวอร์ชันที่สร้างออบเจ๊คต์ Rectangle และให้ค่าออกมาเป็นออบเจ๊คต์ใหม่ แทนการแก้ไขออบเจ๊คต์เดิม

15.7 การดีบัก

เมื่อเราเริ่มทำงานกับออบเจ๊คต์ เรามีแนวโน้มที่จะพบเอ็กเซ็ปชั่นใหม่ๆ ถ้าเราพยายามเข้าถึงแอตทริบิวต์ที่ไม่มีอยู่จริง เราจะพบเอ็กเซ็ปชั่น AttributeError

>>> p = Point()
>>> p.x = 3
>>> p.y = 4
>>> p.z
AttributeError: Point instance has no attribute 'z'

ถ้าเราไม่แน่ใจว่าออบเจ๊คต์นั้นเป็นชนิดใด เราสามารถสอบถาม

>>> type(p)
<class '__main__.Point'>

เรายังสามารถใช้ isinstance เพื่อตรวจสอบว่าออบเจ๊คต์นั้นเป็นอินสแตนซ์ของคลาสใดคลาสหนึ่งหรือไม่

>>> isinstance(p, Point)
True

ถ้าเราไม่แน่ใจว่าออบเจ๊คต์หนึ่งมีแอตทริบิวต์ที่สนใจหรือไม่ เราสามารถใช้ฟังก์ชัน hasattr ตรวจสอบได้

>>> hasattr(p, 'x')
True
>>> hasattr(p, 'z')
False

อาร์กูเมนต์แรกคือออบเจ๊คต์ใดๆ อาร์กูเมนต์ที่สองเป็น สตริง ที่มีชื่อของแอตทริบิวต์ที่ต้องการทราบ

นอกจากนี้เรายังสามารถใช้คำสั่ง try เพื่อหาดูว่าออบเจ๊คต์นั้นมีแอตทริบิวต์ที่เราต้องการหรือไม่

try:
    x = p.x
except AttributeError:
    x = 0

วิธีนี้สามารถช่วยทำให้การเขียนฟังก์ชันเพื่อทำงานกับหลากหลายชนิดข้อมูลได้ ดูรายละเอียดเพิ่มเติมในหัวข้อ 17.9

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

  • คลาส (class): ชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดเอง การประกาศคลาสจะสร้างคลาสออบเจ๊คต์ใหม่ขึ้น
  • คลาสออบเจ๊คต์ (class object): ออบเจ๊คต์ที่มีรายละเอียดของชนิดข้อมูลที่ผู้เขียนโปรแกรมกำหนดขึ้นเอง คลาสออบเจ๊คต์สามารถใช้สร้างอินแสตนซ์ของชนิดข้อมูลนั้นได้
  • อินสแตนซ์ (instance): ออบเจ๊คต์ที่เป็นสังกัดของคลาส
  • สร้างอินสแตนซ์ (instantiate): การสร้างออบเจ๊คต์ใหม่
  • แอตทริบิวต์ (attribute): ชื่อพร้อมกับค่าซึ่งผูกอยู่กับออบเจ๊คต์
  • ออบเจ๊คต์ฝังตัว (embedded object): ออบเจ๊คต์ที่ถูกเก็บเป็นแอตทริบิวต์ของออบเจ๊คต์อื่น
  • สำเนาตื้น (shallow copy): การสำเนาข้อมูลภายในออบเจ๊คต์รวมถึงแอตทริบิวต์ที่เป็นอ้างอิงไปยังออบเจ๊คต์ฝังตัว ทำได้โดยการเรียกใช้ฟังก์ชัน copy ที่อยู่ในโมดูล copy
  • สำเนาลึก (deep copy): การสำเนาข้อมูลภายในออบเจ๊คต์รวมทั้งข้อมูลของออบเจ๊คต์ที่เป็นออบเจ๊คต์ฝังตัวตลอดจนข้อมูลออบเจ๊คต์อื่นที่ถูกอ้างอิงด้วยออบเจ๊คต์ฝังตัวเหล่านั้นไปเรื่อยๆ ทำได้โดยการเรียกใช้ฟังก์ชัน deepcopy ที่อยู่ในโมดูล copy
  • แผนภาพออบเจ๊คต์ (object diagram): แผนภาพที่มีการแสดงออบเจ๊คต์กับแอตทริบิวต์ภายในและค่าของแต่ละแอตทริบิวต์

15.9 แบบฝึกหัด

แบบฝึกหัด 1
เขียนนิยามของคลาสชื่อ Circle ที่มีแอตทริบิวต์ center และ radius กำหนดให้ center เป็นออบเจ๊คต์ของ Point และให้ radius เป็นตัวเลข

สร้างอินสแตนซ์ของ Circle ซึ่งเป็นตัวแทนของวงกลมซึ่งมีจุดศูนย์กลางอยู่ที่ (150, 100) รัศมี 75 หน่วย

เขียนฟังก์ชันชื่อ point_in_circle ซึ่งรับออบเจ๊คต์ Circle และ Point แล้วให้ค่าคืนกลับเป็น True ถ้าจุดนั้นอยู่ภายในหรือบนขอบเขตของวงกลม

เขียนฟังก์ชันชื่อ rect_in_circle ซึ่งรับออบเจ๊คต์ Circle และ Rectangle แล้วให้ค่าคืนกลับเป็น True ถ้ารูปสี่เหลี่ยมนั้นอยู่ภายในหรือบนขอบเขตของวงกลม

เขียนฟังก์ชันชื่อ rect_circle_overlap ซึ่งรับออบเจ๊คต์ Circle และ Rectangle แล้วให้ค่าคืนกลับเป็น True ถ้ามุมใดมุมหนึ่งของสี่เหลี่ยมอยู่ภายในวงกลม หรือเขียนรุ่นที่ท้าทายมากขึ้นซึ่งจะให้ค่าออกมาเป็น True ถ้าส่วนใดส่วนหนึ่งของรูปสี่เหลี่ยมอยู่ภายในวงกลม

เฉลย: http://thinkpython2.com/code/Circle.py

แบบฝึกหัด 2
เขียนฟังก์ชันชื่อ draw_rect ซึ่งรับออบเจ๊คต์ Turtle และ Rectangle แล้วใช้ออบเจ๊คต์ Turtle ในการวาดรูปสี่เหลี่ยมโดยดูตัวอย่างการใช้งาน Turtle ในบทที่ 4

เขียนฟังก์ชันชื่อ draw_circle ซึ่งรับออบเจ๊คต์ Turtle และ Circle แล้ววาดรูปของวงกลมนั้น

เฉลย: http://thinkpython2.com/code/draw.py

https://greenteapress.com/thinkpython2/html/thinkpython2016.html

python/class_objects.txt · Last modified: 2021/08/30 09:55 (external edit)

Page Tools