Python – Operator Overloading

operator overloading
ตัวดำเนินการ(operator) ทำงานกับ built-in class แต่ทำงานแตกต่างกันตามชนิดของข้อมูล ตัวอย่างเช่น ตัวดำเนินการ “+” จะดำเนินการเพื่อบวกจำนวน 2 จำนวน หรือ อาจจะใช้เพื่อทำการเชื่อมข้อความ 2 ข้อความก็ได้ คุณลักษณะเช่นนี้ ซึ่งอนุญาติให้ตัวดำเนินการสามารถทำงานแตกต่างกันตามบริบทของข้อมูล จะเรียกว่า operator overloadding

/**
 * Insert your code here
 */

ผลที่ได้

Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

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

special function
ฟังก์ชันของคลาสที่เริ่มต้นด้วย “__” นั้นจะถูกเรียกว่า special function เช่น ฟังก์ชัน __init__ ซึ่งจะถูกเรียกขึ้นถูกครั้งที่มีการสร้างออบเจ็คใหม่จากคลาสนั้นๆ การกำหนด special function นี้ จะทำให้คลาสที่เราทำการสร้างขึ้นมาใหม่นั้น compatible กับ built-in class
ตัวอย่างการใช้งาน

p1 = Point(2, 3)
print( p1 )

ผลที่ได้

<__main__.Point object at 0x00000000031F8CC0>

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

class Point:
     def __init__(self, x=0, y=0):
          self.x = x
          self.y = y

     def __str__(self):
          return "({0}, {1})".format(self.x, self.y)

p1 = Point(1, 5)
print( p1 )

ผลที่ได้

(1, 5)

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

class Point:
     def __init__(self, x=0, y=0):
          self.x = x
          self.y = y

     def __str__(self):
          return "({0}, {1})".format(self.x, self.y)

p1 = Point(5, -2)
print( str(p1) )
print( format(p1) )

ผลที่ได้

(5, -2)
(5, -2)

เมื่อเราใช้คำสั่ง str( p1 ) หรือ format( p1 ) ไพธอนเองจะเรียกใช้ฟังก์ชัน p1.__str__() ภายในอัตโนมัติ

การ overloading กับตัวดำเนินการ “+”
หากต้องการจะทำการ overload จากตัวดำเนินการ “+” เราต้องทำการ implement ฟังก์ชัน __add__() ในคลาสที่เราทำการสร้างขึ้นมาใหม่ เราสามารถกำหนดเราละเอียดอย่างไรก็ได้ภายในฟังก์ชัน แต่เมื่อต้องทำการคืนค่าออกมาต้องคืนค่าออกมาในรูปแบบของออบเจ็ค Point ซึ่งเป็นผลรวมของข้อมูลทั้งสองที่ส่งเข้าไปดำเนินการ

class Point:
     def __init__(self, x=0, y=0):
          self.x = x
          self.y = y

     def __str__(self):
          return "({0}, {1})".format(self.x, self.y)

     def __add__(self, other):
          x = self.x + other.x
          y = self.y + other.y
          return Point(x, y)

point1 = Point(5, -2)
point2 = Point(3, 7)
result = point1 + point2
print( result )

ผลที่ได้

(2, 5)

จากตัวอย่าง เมื่อเราทำการคำนวณผลรวมของข้อมูล point1+point2 ไพธอนจะเรียกฟังก์ชัน point1.__add__(point2) ซึ่งก็เทียบเท่า Point.__add__(point1, point2) ในทำนองเดียวกัน เราสามารถที่จะทำการ overload เครื่องหมายดำเนินการอื่นๆ ได้อีกและ special function ที่เราจำเป็นต้องระบุรายละเอียดสำหรับตัวดำเนินการนั้นๆ ดังรายละเอียดในตารางด้านล่าง

Operator Expression Internally
Addition p1 + p2 p1.__add__(p2)
Subtraction p1 – p2 p1.__sub__(p2)
Multiplication p1 * p2 p1.__mul__(p2)
Power p1 ** p2 p1.__pow__(p2)
Division p1 / p2 p1.__truediv__(p2)
Floor Division p1 // p2 p1.__floordiv__(p2)
Remainder (modulo) p1 % p2 p1.__mod__(p2)
Bitwise left shift p1 << p2 p1.__lshift__(p2)
Bitwise right shift p1 >> p2 p1.__rshift__(p2)
Bitwise AND p1 & p2 p1.__and__(p2)
Bitwise OR p1 | p2 p1.__or__(p2)
Bitwise XOR p1 ^ p2 p1.__xor__(p2)
Bitwise NOT ~p1 p1.__invert__()

Overloading comparison operator
ไพธอนไม่ได้จำกัด operator overloading เฉพาะการดำเนินการทางคณิตศาสาตร์ เราสามารถจะ overload สำหรับตัวดำเนินการเปรียบเทียบได้เช่นกัน โดยหากเราต้องการจะทำงานกับเครื่องหมาย < ในคลาส Point โดยจะทำการเปรียบเทียบ magnitude ของ Point เหล่านี้กับจุดเริ่มต้น และคืนค่าเป็นผลจากคำนวณดังกล่าว
ตัวอย่างการใช้งาน

class Point:
     def __init__(self, x=0, y=0):
          self.x = x
          self.y = y

     def __str__(self):
          return "({0}, {1})".format(self.x, self.y)

     def __lt__(self, other):
          self_mag = (self.x ** 2) + (self.y ** 2)
          other_mag = (other.x ** 2) + (other.y ** 2)
          return self_mag < other_mag

print( Point(1, 1) < Point(-2, -3) )
# True

print( Point(1,1) < Point(0.5,-0.2) )
#False

print( Point(1,1) < Point(1,1) )
#False

สำหรับ special function ที่เราต้องการ implement เพื่อจะ overload การดำเนินการเปรียบเทียบ ตามรายละเอียดตารางด้านล่าง

Operator Expression Internally
Less than p1 < p2 p1.__lt__(p2)
Less than or equal to p1 <= p2 p1.__le__(p2)
Equal to p1 == p2 p1.__eq__(p2)
Not equal to p1 != p2 p1.__ne__(p2)
Greater than p1 > p2 p1.__gt__(p2)
Greater than or equal to p1 >= p2 p1.__ge__(p2)