The Art of Simplicity: Python’s Syntactic Sugar?

Thohirah.
odds.team
Published in
4 min readMay 24, 2024

“ถ้าเราพูดถึง Code ที่ดี เราจะให้คำนิยามมันว่าอะไร?”

เมื่อเราเริ่มเขียน Code นั่นหมายความว่าเรากำลังแก้ไขปัญหาบางอย่าง ไม่ว่าจะเป็น Implement Featureใหม่ หรือแก้ Bug ที่ผ่านมา

เป้าหมายหลักของ Code คือ มันต้องทำงานได้ตรงตามโจทย์ที่ต้องการแก้ไข

Good code, It should work and It should keep working.

การที่ Code ณ ขณะนั้น “ทำงานได้” เป็นเรื่องสำคัญเรื่องนึง
และการที่มันทำหน้าที่ของมันได้อย่างสง่างามก็เป็นเรื่องสำคัญอีกเรื่องเช่นกัน

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

Table of content

🍭 Syntactic Sugar - ความหมายโดยสังเขป
🧩 Minimum Viable Python - Python syntax ที่เราขาดไม่ได้
🔍 Simple Unravelling - เบื้องหลังของ Syntactic Sugar ใน python กันนิดหน่อย
🧁 Add some sugar - Syntactic Sugar ที่ Python มีให้ใช้
💥 Accidental Complexity - ความซับซ้อนที่เราไม่ตั้งใจให้มันเกิด
📝 Conclusion - บทส่งท้าย
A touch of sweetness for your code syntax — readability is the goal.

Syntactic Sugar

Syntactic sugar ไม่ใช่เรื่องใหม่ สิ่งนี้อยู่ในแทบจะทุก Programing Language
คือ Syntax ที่ถูกออกแบบมาเพื่อให้มนุษย์ “อ่านและใช้” ง่ายขึ้น เป็นเหมือน Abstraction แบบนึงที่ทำให้นักเขียน Focus กับโจทย์ที่ถืออยู่โดยไม่ต้องรู้ว่าอะไรอยู่เบื้องหลัง

เปรียบเสมือนการ เพิ่มความหวานเล็กน้อย ให้กับ syntax
เพื่อให้ทำงานได้ง่ายมากขึ้น

Python เป็นหนึ่งภาษาที่มี Level ของ Abstraction สูงประมาณนึงและมีสิ่งที่เรียกว่า Syntactic Sugar ให้ใช้เยอะมากเช่นกัน

MVPy?

Minimum Viable Python (MVPy)โดย Brett Cannon

Brett Cannon — Python Core Contributor ใช้เวลาช่วง Covid ไปกับการ Research ว่าภายใต้ Python syntax ที่ใช้กันอย่างแพร่หลาย เบื้องหลังมันทำงานอย่างไรบ้าง ใน Blog post เขาอธิบายไปถึง Opcode (Operation Code) และ Cpython เลยตามไปขยายความกันเพิ่มได้ที่ Python’s syntac sugar

ปรากฏว่าหา syntax ที่ ไม่สามารถ Rewrite ได้นั้นง่ายกว่า
การทวนดู syntax ที่เรา Rewrite ได้ซะอีก!
ฉะนั้น Python จึงหวานมากประมาณนึง

คุณ Brett บอกว่า อย่างน้อยที่สุดในมุมมองของเขา เราขาด Syntax เหล่านี้ไม่ได้
เขาจึงเรียกสิ่งเหล่านี้ว่า Minimum Viable Python (MVPy)

1.  Integers (เป็น base ของ literals อื่นๆ เช่น bytes หรือ string)
2. Function calls
3. Assignment =
4. Function definitions (def)
5. nonlocal
6. return
7. yield
8. try/except
9. raise
10. while

ส่วน syntax อื่นๆ นอกเหนือจาก 10 อย่างนี้ สามารถถูกเขียนใหม่ได้ด้วย Combination ของทั้ง 10 syntax ด้านบนได้เลยถ้า..
มีเวลาและความอดทนมากเพียงพอ 😉

เช่นถ้าเราไม่ใช้ ifเราสามารถหยิบ while มาทำ conditional statement ได้

if A:
B
class _Done(Exception):
pass

try:
while A:
B
raise _Done
except _Done:
pass

Simple Unravelling

ภายใต้ python syntax ส่วนใหญ่เป็น function call ชุดนึงเลย แต่ว่า…
มันสำคัญยังไงกันนะ?
เราสามารถเขียน code แบบมี syntax sugar ได้โดยไม่จำเป็นต้องรู้เบื้องหลังการทำงานของมัน แต่การเข้าใจวิธีการทำงานจะมีประโยชน์มากเมื่อต้องแก้ไขปัญหาบางอย่าง มาลองแกะ syntax sugar ที่เราอาจจะเคยใช้โดยไม่รู้ตัวว่ากำลังลิ้มลองของหวานอยู่กันดีกว่า

🌘 Attribute access

x.y

ทุกอย่างใน python คือ object

วิธีการปกติที่เราใช้ get attribute ของ object นั้น ดูเรียบง่ายมาก แต่เบื้องหลังมีกลไกการทำงานที่ซับซ้อนประมาณนึง

Attribute access ถูก implement ด้วย 2 special method
(และเป็นเพราะทุกอย่างใน python คือ object นั่นหมายความว่า special method เหล่านั้นถูกห่อหุ้มไว้เบื้องหลังอยู่เสมอ)

หนึ่งคือ __getattribute__()ซึ่งจะถูกเรียกทุกครั้งที่เราพยายาม access attribute ใดก็ตาม สองคือ __getattr__()จะถูกเรียกอีกทีเวลาที่ __getattribute__() raise error AttributeError ออกมา

object.__getattribute__()
สิ่งสำคัญอย่างแรกที่จะเกิดจาก object.getattribute() คือการมองหา data descriptor บน class หรือ type ของ instance นั้น
ถ้าหา data descriptor บน type นั้นไม่เจอ ที่ต่อไปในการตามหาก็คือบน object หรือ instance นั้นโดยตรง จาก __dict__ ที่จะเก็บ attribute ของ object นั้นๆในรูปของ python dictionary

แต่เราไม่จำเป็นต้องใช้ __getattribute__ เพราะเราสามารถ object.attribute ออกมาได้เลยและดูอ่านง่ายกว่าด้วย นี่คือสิ่งที่เรียกว่า syntactic sugar

🌘 Import Statement

import a

import เป็น syntactic sugar แบบนึงที่เราหลีกเลี่ยงไม่ได้เลยเวลาเราจะเรียกใช้ module ต่างๆ โดยที่แท้จริงแล้วเบื้องหลังการ import ทุกครั้งจะมีหน้าตาประมาณนี้

a = __import__("a", globals(), locals())

สิ่งที่อาจจะดูประหลาดตรงนี้คือ ตรงนี้มีการ call globals() และ locals() ด้วย ในกรณีนี้อาจจะดูไม่จำเป็น แต่ถ้า import submodule จะทำให้เห็นภาพชัดขึ้น

import a.b

a = __import__("a.b", globals(), locals())

เราจะเห็นว่าการ import a.b จริงๆแล้วคือการทำให้มั่นใจว่า .b มีอยู่จริงใน module a เพราะท้ายที่สุดแล้ว __import__ ก็จะ return เพียงแค่ module a ออกมาเท่านั้น

from ... import ...
ถ้าหาก from a.b import c ในกรณีนี้เบื้องหลังจะมีหน้าตาประมาณนี้

c = __import__('a.b', globals(), locals(), ['c']).c

สิ่งที่เราสังเกตได้ชัดถ้าเทียบกับ import a คือ argument ['c']
ครั้งนี้ใน __import__ จะทำให้มั่นใจว่า object a.b นั้นมี c อยู่แต่ยัง return เพียงแค่ a.b กลับมาเท่านั้น หมายความเราก็ยังต้อง access atrribute c เองอยู่ดี
▶️ การใช้ from a.b import c จึงสะดวกกว่ามาก

ถ้าในแต่ละวันอันสดใสของเราต้องมาเขียน import แบบที่ไม่มี syntax sugar คงต้องมีหม่นหมองกันบ้างพอดี

นอกจากนี้ก็ยังมี ตัวอย่างอื่นๆ ของ python syntactic sugar เช่นและอีกมากมายสามารถตามกลับไปดูเบื้องลึกเบื้องหลังเพิ่มได้

Thinking about how complexity python hiding from user and
flexibility for us in some case we need

Add some sugar

หลังจากลองดูการทำงานเบื้องหลังมานิดนึงแล้ว
ลองมาดู syntax sugar ที่ python มีให้ใช้กันสักนิด

🌔 Lambda function

เราสามารถเขียน anonymous function ตัวน้อยๆ มาใช้ได้โดย ประกาศ lambda ไว้รับ argument เข้ามาและกระทำการบางอย่าง

# Define a lambda function to convert Celsius to Fahrenheit
celsius_to_fahrenheit = lambda celsius: (celsius * 9/5) + 32

# Using the lambda function
celsius_temperature = 30
fahrenheit_temperature = celsius_to_fahrenheit(celsius_temperature)

🌔 List Comprehension

One Liner List: ด้วย List comprehension เราสามารถวาง for loop และ if condition ไว้ในบรรทัดเดียวกันแล้วครอบด้วย bracket ได้เลย

list_of_numbers = [1, 2, 3, 4, 5, 6]

odds = [num for num in list_of_numbers if num % 2 != 0]
evens = [num for num in list_of_numbers if num % 2 == 0]

🌔 Ternary Conditional Operators

One Liner If and Else

number = 7
parity = "even" if number % 2 == 0 else "odd"
print(parity) # Output: "odd"

แทบจะทุก syntax ของ python ที่เป็น syntactic sugar ทั้งนี้ก็เพื่อความแม่นยำ สะดวก และ ความง่ายในการ implement software ขึ้นมา
ใจความสำคัญของ Syntactic sugar ยังคงเป็น simplicity ในการอ่านและการสร้าง feature ขึ้นมาอยู่เสมอ

Accidental Complexity

หวานมากไปไม่ดีนะ
Syntactic sugar มีบทบาทสำคัญให้เราได้เขียน Code ที่อ่านง่ายขึ้น สะดวกขึ้น
แต่บางครั้งถ้าเราไม่ทันระวังตัว กลับกลายเป็นว่าเราเลี้ยงสัตว์ประหลาดรอวันผงาดเด่นในดินแดน

Syntactic sugar เกิดขึ้นมาเพื่อ Readability ฉะนั้น Focus หลักที่ไม่ควรหายไป
หรือถูกหลงลืมคือ แต่ละครั้งที่เราหยิบอุปกรณ์เหล่านี้มาใช้ Readability ของ code นั้นยังคงหลงเหลืออยู่หรือไม่

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

score = 99

grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "D" if score >= 60 else "F"
print(grade) # Output: "A++"

Code is one form of communication
Us to computer
To people
To our future selves
when our mental model of that line of code has faded away

💡Readability of our systems is a key attribute of our ability

เราจะเห็นว่า Syntax sugar หรือ Abstract level พวกนี้จะยึดโยงอยู่กับภาษาและจะเปลี่ยนไปตาม Paradigm ของภาษานั้นๆ สักวันมันจะไม่นิยมอีกแล้วและจากเราไป แต่สิ่งที่จะยังคงอยู่คืออะไรที่มันเรียบง่าย

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

คำว่านิยม มันจะยึดอยู่กับเวลา แต่ละช่วงมันจะมีความนิยมที่ต่างกัน
แสดงว่าถ้าเราไม่มีนิยม มันก็มีโอกาสที่จะไม่เกี่ยวกับเวลา
และอะไรที่ไม่เกี่ยวกับเวลา มันมีนัย คำว่า Timeless

ผศ. พิรัส พัชรเศวต อาจารย์และอดีตหัวหน้าภาควิชาสถาปัตยกรรมศาสตร์ จุฬาลงกรณ์มหาวิทยาลัย

สิ่งที่เราทุกคนทำได้คือหาคำตอบให้กับตัวเองว่า ในพื้นที่ของ
Software Development มันมีสิ่งที่ถูกเรียกว่า Timeless อยู่จริงหรือเปล่า?

ป้ายยา~

ขอขอบคุณพี่เจที่ทำให้ Direction ชัดเจนมากขึ้น และพี่หมิวพี่เอ็มพี่ต้อมที่ช่วย auditให้ค่ะ 🙇🏼‍♀️

--

--