feature-python-oop
feature-python-oop

มาถึงยุคนี้คงไม่ต้องบอกกันแล้วใช่มั้ยว่า Object-Oriented Programming (OOP) คืออะไร? 🙂

เอาง่ายๆมันก็คือมองทุกอย่าง Object โดยมีหลักการ 3 อย่างคือ

  1. encapsulation (การห่อหุ้มค่าของสิ่งต่างๆเอาไว้ หรือง่ายๆก็พวก attribute ภายใน class นั้นแหละ private,public อะไรก็ว่าไป)
  2. inheritance (การสืบทอดของคุณสมบัติต่างๆเช่นพวก methods และ variable ของ class แม่ เอาไป resuse ง่ายๆ เช่น Animal มีลูกเป็น Dog ซึ่งสัตว์ก็จะต้องกิน เพราะฉะนั้นหมาก็ต้องกินเป็นต้น )
  3. polymorphism (การเปลี่ยนแปลงไปในรูปแบบต่างๆแต่มีคุณสมบัติมาจาก class แม่มัน เช่น class animal เป็นแม่ แล้วก็มี cat กับ bird เป็นลูก ซึ่งมีวิธีการกินแตกต่างกันนั้นแหละ)

แค่นี้แหละ เนื้อหาลึกๆไปหาอ่านเอาน่ะ มีบน Internet เยอะแยะไปหมดเลยละ ขีเกรีย้จพิมย์มันซํ้า

จริงๆจุดประสงค์บทความนี้เลยคือเตือนความจําเรื่องของการใช้ Python ในรูปแบบของ OOP เพราะส่วนใหญ่ที่เขียนๆๆ Robot Framework กันมา เราจะเขียนกันแบบเป็น Script กันซะมากกว่า เดี๋ยวจะลืมไปว่ามันมีคุณสมบัติไรกันบ้างเวลาใช้งานจริงๆ

Example Python Class

ตัวอย่างของหน้าตา class ก็ทั่วๆไปเนอะ แบบเราเขียนกันประจํา

python-example-oop-class
python-example-oop-class

เขียน python อย่าลืมเรื่อง tab กับ indent น่ะ เรื่องหลักเลยล่ะ 🙂

ใน code ตัวอย่างง่ายๆ แต่เราสามารถแบ่งแยกออกมาเป็นจุดน่าสนใจได้ถึง 3 จุดคือ

  1. __init__
  2. self
  3. attribute

__init__

มือใหม่ตอนเขียน Python แรกๆเชื่อเลยว่าทุกคนต้องคิดว่า __init__() เนี่ยคือ constructor แน่เลย เพราะทุกคนเห็นมันโดนเรียกทุกครั้งตอนที่เราสร้าง object แต่ๆ เดี๋ยวก่อนนะ จริงๆแล้วมันถูกเรียกหลังจากที่ Object ถูกสร้างต่างหากละ เพียงแต่เป็น method แรกที่เรียกหลังจาก new object ต่างหาก

python-new-object-example
python-new-object-example (https://www.programiz.com/article/python-self-why)

จจากรูปตัวอย่างเนอะ จะเห็นว่าจริงๆเรามี Method New ที่ถูกเรียกเป็นตัวแรกเสมอแหละ เพียงแต่ว่าปกติแล้ว เราไม่ได้จําเป็นต้อง override มัน เราเลยไม่ได้เรียกใช้อะ แล้วเข้า __init__() มาเลยนั้นเอง

self

จริงๆแล้ว self น่ะมันเป็นแค่ naming convension แค่นั้นแหละ สําหรับ python เพื่อแยกแยะถึง instance attributes และ methods กับ local variables ไม่ได้มีไรลึกลับซับซ้อนเลย เช่น

example-python-local-variables
example-python-local-variables (https://www.programiz.com/article/python-self-why)

ถ้าเราเคยเขียนภาษาแบบ Java มาเราจะคุ้นเคยกับ this keyword ซึ่งจริงๆแล้วมีค่าเท่ากันแหละ refer ถึง instance object ใดๆแค่นั้นเอง

อย่างตัวอย่างข้างล่างนี้

example-self-python
example-self-python (https://pythontips.com/2013/08/07/the-self-variable-in-python-explained/)

เจ้าตัวนี้เป็นตัวอย่างง่ายๆสําหรับการใช้ self กับ attribute bankrupt จะเห็นว่า variable เป็น false แล้วพอเรา set ค่ากับ get ค่าดังตัวอย่างข้างล่าง มันจะได้ผลลัพธ์ที่แตกต่างกัน

example-python-instance-attribute
example-python-instance-attribute

นั้นเป็นเพราะว่ามันเป็นตัวแปรของคนละ instance กันนั้นเอง 🙂 แค่นั้นแหละ ไม่ใช่เรื่องยากเลย

เรื่องน่ารู้คือ เวลาเราเรียก x = Restaurant() แต่จริงๆแล้วน่ะมันก็คือ Restaurant().bankrupt น่ะ (จริงๆก็เรื่องพื้นฐานและแต่เขียนไว้ดีกว่า)

ส่วนอีกตัวอย่างนึงที่เกี่ยวกับ self คือด้านล่างนี้

myobject.method(arg1,arg2) ซึ่งมันจะโดน convert โดย python ให้กลายเป็น Myclass.method(myobject,arg1,arg2) แทน โดยเจ้า myobject ไปแทน self นั้นเอง

มันจะถูก convert อัตโนมัติเลยเป็นการ reference แบบนึง

Attribute

attribute ใน python อาจจะทําให้เรา งง นิดหน่อยด้วยความที่ต้องปรับหัวเล็กน้อยจาก Java มาเป็น Python

example-self-python
example-self-python

ยกตัวอย่างอย่าง class Restaurant นี้จะมี attribute ตัวนึงชื่อ Bankrupt ทําหน้าที่เก็บ Value “False” ไว้

ซึ่งเจ้าตัวนี้ถือเป็น instance attribute ทําให้เราสามารถ access  ได้ตรงๆเลย จากข้างนอก เช่น


example = Restaurant()

example.bankrupt #same with Restaurant().bankrupt ผลที่ออกมาก็จะเป็น False

นี้แหละคือเรื่องของ Attribute แบบง่ายๆ

เรามาเรียนรู้ส่วนอื่นๆๆอย่างอื่นบ้างเช่น

  • Getter And Setter
  • Inheritance
  • Polymorphism

Getter And Setter

ใน Python ถ้าจริงๆแล้วตามสไตล์ของ Pythonic Way เลยน่ะ มันไม่มีเรื่องของ Getter หรือ Setter เลยล่ะ เพราะการเขียน Getter และ Setter แบบในภาษาอื่นๆเนี่ยมันใช้ไม่ได้ใน python น่ะ เพราะข้างนอกก็จะสามารถ access แล้ว manipulate value ได้เช่นตัวอย่างข้างล่างนี้เลย


class P(object):
def __init__(self,x):
print('test')
def get_data(self):
return self.x
def set_data(self,x):
self.x = x

print('class P')
pp = P(1)
pp.set_data(2)
print(pp.get_data()) #output = 2
print(pp.x) #output = 2
pp.x = 5
print(pp.get_data()) #output = 5
print(pp.x) #output = 5

ด้วยวิธีนี้เรายังสามารถ access เข้าไปดึงค่า x จากข้างนอกได้ โดยไม่ต้องสนใจ get และ set เลยแม้แต่น้อย ตามตัวอย่างที่โชว์อะ จะเห็นว่าเมื่อเราใช้ pp.x ยังไงก็จะเข้าไปแก้ข้อมูลได้ 🙁 เศร้าเลยอะ ไม่มีการ encapsulation ใดๆ

ไม่ดีๆแบบนี้มันไม่มีประโยชน์ที่จะมี method getter และ setter เลย เราน่าจะใช้


class X(object):
def __init__(self,x):
self.__x = x
def get_data(self):
return self.__x
def set_data(self,x):
self.__x = x

print('class X with private var')
xx = X(1)
xx.set_data(99)
print(xx.get_data()) #output = 99
print(xx.x) #output = AttributeError: 'X' object has no attribute 'x'
xx.x = 5
print(xx.get_data()) #output = 99
print(xx.x) #output = AttributeError: 'X' object has no attribute 'x'

สังเกตุมั้ยว่าเรามีการใช้ “_” หรือ “__” ขึ้นมา ซึ่งมันหมายความว่า private access ทําให้ไม่สามารถเรียกใช้ attribute จากข้างนอกได้นั้นเอง ด้วยวิธีนี้ก็จะทําให้ไม่มีใครมา manipulate data ของเราและเป็น encapsulation concept นั้นเอง

accesibility-python
accesibility-python

เจ้า _ แปลว่าทําให้เป็น private variable ไม่มีใครมีสิทธิ์ access จากภายนอก

ส่วนเบื้องหลังเจ้า __ ก็เหมือกันทําให้เป็น private accesibility แต่ที่แตกต่างกันก็คือมันเอาชื่อ class ไปใส่เพื่อไม่ให้ตัวแปรซํ้ากันเวลา run time

แต่จริงๆแล้ววิธีที่ Pythonic Way แนะนําจริงๆเพื่อใช้ในการจัดการกับ getter และ setter ที่ดีจริงๆแล้วป็นเรื่องของการใช้ decorator pattern ด้วย property 

python-getter-setter-decorator-pattern
python-getter-setter-decorator-pattern

เพียงใส่ property annotation แบบด้านบนก็พร้อมล่ะ ที่จะทําให้มันทํางานเป็น getter และ setter นั้นเอง 🙂

เนื้อหาเกี่ยวกับการใช้ getter and setter

Inheritance

ส่วนของการใช้ inheritance ก็ง่ายมากๆแค่ส่งต่อกันผ่าน Object หลัง class แค่นั้น

stackoverflow-python-inheritance
stackoverflow-python-inheritance (http://stackoverflow.com/questions/3724110/practical-example-of-polymorphism)

ซึ่งใน Python เองสามารถทํา multiple inheritance ได้ด้วย ไม่เหมือน Java 

python-multiple-inheritance
python-multiple-inheritance (https://www.programiz.com/python-programming/multiple-inheritance)

ทีนี้เราก็ได้ habit ของทั้งสองตัวมาเลย

Polymorphism

จริงๆหลักการณ์ polymorphism ที่เราคุ้นเคย น่าจะเป็นการสร้าง interface ตัวนึง แล้ว class ต่างๆก็มา Implement กันลงไปใช่มั้ย?

แต่ python ไม่มี Interface ครัช!!! แล้วก็ไม่จําเป็นต้องไม่มีด้วย 🙂 เพราะใน python มันสามารถ multiple inheritance ได้ ไม่เหมือน Java นั้นเอง

python-polymorphism-example
python-polymorphism-example (http://stackoverflow.com/questions/3724110/practical-example-of-polymorphism)

ดังนั้นถ้าสังเกตุที่ class person มันจะเขียนให้มันคล้าย Interface ว่าให้ raise error แบบ NotImplementedError แบบนั้น เหมือนกันให้เราเอาไป implement เอง (เลียนแบบ Java Interface แหละ)

เราก็แค่ไปใช้แบบนี้


Mart = GradStudent()

Mart.pay_bill()

#ผลก็จะออกมาเป็น Can I owe you ten bucks or do the dishes?

#ถ้าเราไม่ Implement method pay_bill ใน class นี้มันจะไม่ฟ้องเหมือน Java หรอกน่ะ เพราะมันไม่ได้ compile แต่มันจะ error ว่า NotImplemenetedError ตาม class แม่มันก็คือ Person นั้นเอง

สรุปแล้ว

Python OOP ไม่ได้ยาก แถมยืดหยุ่นมากกว่า Java ด้วยซํ้า อาจจะมีหลักๆชวนงงก็พวก self หรือ convention อื่นๆค่อนข้างเยอะเลย แต่ไม่ได้ยากอะไร ซึ่งถ้าอ่านเจ้าสรุปตัวนี้แล้ว เราจะสามารถอ่าน source code ของ python ได้เข้าใจละ 🙂

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.