15 คะแนน โดย GN⁺ 2025-04-22 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • t-string (t-strings) คือ ความสามารถใหม่สำหรับการจัดการสตริงอย่างปลอดภัยและยืดหยุ่น ที่ถูกเพิ่มเข้ามาใน Python 3.14
  • ต่างจาก f-string เดิม โดย t-string จะคืนค่าเป็นอ็อบเจ็กต์ Template แทนสตริง ทำให้ ประมวลผลอย่างปลอดภัยได้โดยไม่ถูกแสดงผลอัตโนมัติ
  • t-string มีโครงสร้างที่ช่วยให้ escape อินพุตแบบไดนามิกสำหรับ HTML, SQL ฯลฯ ได้อย่างปลอดภัย
  • เป็นแนวคิดที่คล้ายกับ tagged templates ของ JavaScript และสามารถขยายการแปลงและการประมวลผลได้หลากหลาย
  • หาก ecosystem ของเครื่องมือพัฒนา Python รองรับฟีเจอร์นี้ได้ดี ก็อาจสร้างความเปลี่ยนแปลงครั้งใหญ่ต่อ แนวทางการจัดการสตริงที่เน้นเว็บ/ความปลอดภัย

ฟีเจอร์ใหม่ของ Python: t-string (Template Strings)

  • ตั้งแต่ Python 3.14 เป็นต้นไป จะมีการเพิ่ม Template string (t-strings) เป็นฟีเจอร์ทางการ โดยใช้ไวยากรณ์ t"..."
  • ต่างจาก f-string เดิม t-string จะถูกประเมินค่าเป็นอ็อบเจ็กต์ string.templatelib.Template ไม่ใช่สตริงในทันที
  • อ็อบเจ็กต์นี้ต้องผ่าน กระบวนการแปลงเพิ่มเติม ก่อนนำไปแสดงผล ซึ่งทำให้สามารถ จัดการและแปลงค่าที่เป็นไดนามิกได้อย่างปลอดภัย

ทำไม f-string จึงอาจมีความเสี่ยง?

  • f-string ถูกประเมินค่าเป็นสตริงทันที จึงอาจทำให้เกิด SQL Injection หรือ XSS ในโค้ดที่มีอินพุตจากผู้ใช้
    • ตัวอย่าง: f"<div>{user_input}</div>" → โค้ดโจมตีอาจถูกแทรกเข้าไปได้โดยตรง
  • t-string จะเลื่อนการประเมินค่านี้ออกไป ทำให้ ต้องผ่านการประมวลผลอย่างชัดเจนก่อนจึงจะใช้งานได้

ตัวอย่างการใช้งาน t-string

  • ตัวอย่างการทำ HTML escape:

    evil = "<script>alert('bad')</script>"  
    template = t"<p>{evil}</p>"  
    safe = html(template)  
    # safe คือ "<p>&lt;script&gt;alert('bad')&lt;/script&gt;</p>"  
    
  • รองรับการประมวลผลที่ซับซ้อนขึ้น เช่น การแทรก attributes อัตโนมัติ:

    attributes = {"src": "roquefort.jpg", "alt": "Yum"}  
    template = t"<img {attributes} />"  
    element = html(template)  
    # ผลลัพธ์: "<img src='roquefort.jpg' alt='Yum' />"  
    

โครงสร้างและ API

  • อ็อบเจ็กต์ Template มีพร็อพเพอร์ตี .strings, .values สำหรับแยกข้อความต้นฉบับและค่าที่ถูกแทนลงไป

  • พร็อพเพอร์ตี interpolations ช่วยให้เข้าถึงรายละเอียดการฟอร์แมตอย่าง !s, :>8 ได้ด้วย

  • สามารถวนซ้ำ (iteration) เพื่อประมวลผลข้อความและค่าที่ผสมกันอยู่ได้โดยตรง

  • สร้างด้วยตนเองก็ได้เช่นกัน:

    from string.templatelib import Template, Interpolation  
    template = Template(  
      "Hello ",  
      Interpolation(value="World", expression="name"),  
      "!"  
    )  
    

ตัวอย่างสนุก ๆ: ตัวแปลง Pig Latin

  • ตัวอย่างที่วนดูเนื้อหาของอ็อบเจ็กต์ Template แล้วแปลงคำเป็น Pig Latin:

    def pig_latin(template: Template) -> str:  
        ...  
    name = "world"  
    template = t"Hello {name}!"  
    assert pig_latin(template) == "Hello orldway!"  
    

ทิศทางการพัฒนาต่อไป

  • t-strings อาจนำ ความปลอดภัยและความสามารถในการขยาย มาสู่แนวทางการจัดการสตริงที่เน้นเว็บ/ความปลอดภัย
  • คาดหวังให้เครื่องมือพัฒนาอย่าง black, ruff, VS Code รองรับการฟอร์แมต/ไฮไลต์ของ t-string
  • เนื่องจากมีความคล้ายกับ tagged template ที่นักพัฒนา JavaScript คุ้นเคย จึงมีแนวโน้มถูกนำไปใช้ในหลายเฟรมเวิร์กได้มาก

ความร่วมมือกับชุมชนนักพัฒนา

  • ฟีเจอร์นี้เกิดขึ้นได้จากการมีส่วนร่วมและความร่วมมือของสมาชิกชุมชน Python จำนวนมาก
  • มีการกล่าวถึงการทำงานร่วมกับบุคคลสำคัญอย่าง Jim, Paul, Koudai, Lysandros และ Guido โดยเฉพาะ
  • สามารถดู PEP 750 และคลังตัวอย่างได้ที่ GitHub

ฟีเจอร์ t-string ของ Python 3.14 เป็นก้าวสำคัญที่ยกระดับทั้ง ความปลอดภัยและความสามารถในการขยาย ของสตริง พร้อมก้าวข้ามข้อจำกัดของ f-string แบบเดิม

2 ความคิดเห็น

 
GN⁺ 2025-04-22
ความคิดเห็นบน Hacker News
  • โดยรวมแล้ว ฟีเจอร์นี้ค่อนข้างเจ๋ง โดยพื้นฐานแล้วมันเปลี่ยนโค้ดแบบนี้

    db.execute("QUERY WHERE name = ?", (name,))
    

    ให้กลายเป็นแบบนี้

    db.execute(t"QUERY WHERE name = {name}")
    

    มีคำถามว่าน้ำตาลไวยากรณ์นี้คุ้มค่ากับความซับซ้อนของฟีเจอร์ภาษาใหม่หรือไม่ ผมคิดว่าในกรณีนี้คุ้มด้วยเหตุผลสองข้อ

    • การเปิดโอกาสให้นักพัฒนาไลบรารีทำอะไรก็ได้ตามต้องการผ่านการขยาย {} เป็นเรื่องที่ดี และน่าจะก่อให้เกิด use case ที่ดีได้
    • การทำให้ไวยากรณ์เทมเพลตเป็นแบบทั่วไปทั่วทั้งภาษา เพื่อให้ทุกไลบรารีแก้ปัญหาในลักษณะเดียวกัน ก็น่าจะเป็นเรื่องที่ดี
  • อีกอย่างหนึ่งคือหวังว่า ecosystem ของเครื่องมือจะปรับตัวมารองรับ t-strings เช่น อยากให้ black กับ ruff จัดรูปแบบเนื้อหาใน t-string และให้ vscode ทำสีเนื้อหาประเภททั่วไปอย่าง HTML หรือ SQL ได้

    • มุมมองต่อ t-strings แบบนี้แปลกมาก วิธีเดียวที่จะอนุมานได้ว่าสตริงเทมเพลตควรถูกแปลงเป็น HTML หรือ SQL ที่ถูกต้องคืออิงจากไวยากรณ์ที่เห็นได้ชัดของสตริงเอง ซึ่งทำได้แค่แบบเฉพาะหน้าเท่านั้นและไม่ได้เกี่ยวอะไรกับฟีเจอร์สตริงเทมเพลต
    • จากวิธีที่ฟีเจอร์ถูกออกแบบ ตัวสตริงเองไม่ได้บอกเลยว่าเป็นคอนเทนต์ประเภทไหน หรือท้ายที่สุดจะถูกแปลงเป็นอะไร ทุกอย่างถูกจัดการโดยฟังก์ชันแปลง
    • อย่างที่คนอื่นเสริมไว้ บางอย่างอย่าง sql”select * from {table}” อาจทำแบบนั้นได้ แต่ก็ไม่มีอะไรรับประกันว่าสิ่งที่อยู่ในเทมเพลตจะถูกฟังก์ชันแปลงให้เป็น sql ที่ถูกต้อง t“give me {table} but only {columns}” ก็อาจถูกแปลงเป็น sql ที่ถูกต้องได้หลังจากประมวลผลเทมเพลตแล้ว
  • จะสามารถใช้ไวยากรณ์ SQL ที่สะอาดแบบนี้ได้ไหม?

    city = 'London'
    min_age = 21
    # Find all users in London who are 21 or older:
    users = db.get(t'
      SELECT * FROM users
      WHERE city={city} AND age>{min_age}
    ')
    

    ถ้าฟังก์ชัน db.get() รองรับเทมเพลต ก็ทำได้ นี่น่าจะเป็นวิธีใช้ SQL ที่สะอาดที่สุดเท่าที่เคยเห็นมา

  • ส่วนตัวแล้ว ผมรู้สึกว่าฟีเจอร์นี้โฟกัสกับปัญหาเฉพาะทางมากเกินไปกว่าจะเป็นฟีเจอร์ทั่วไปได้ Python ใหญ่ขึ้นเรื่อย ๆ ถ้ามีคนถามว่า Python ยังเรียนง่ายและเรียบง่ายอยู่ไหม ก็คงต้องตอบว่า "พื้นฐานน่ะใช่ แต่ถ้าจะเรียนรู้ทั้งภาษาจริง ๆ ไม่ใช่"

    • ในแง่นี้ Go น่าสนใจเพราะมันแทบจะปฏิเสธทุกฟีเจอร์ใหม่ทั้งหมด พูดตรง ๆ ว่าผมยังไม่แน่ใจว่า generics คุ้มค่าหรือไม่ เพราะมันเพิ่มความซับซ้อนเยอะ แนวคิดทั่วไปที่พยายามทำให้ภาษายังยึดอยู่กับจุดโฟกัสดั้งเดิมนั้นน่าจะถูกต้อง C++ คงเป็นกรณีสุดโต่งที่ตัวภาษาแทบไม่เหมือนตอนเริ่มต้นแล้ว
  • การถกเถียงครั้งใหญ่ (414 คะแนน, 10 วันที่แล้ว, 324 ความคิดเห็น) ลิงก์

  • ค่อนข้างเจ๋ง ถ้าจะพอร์ตฟีเจอร์จาก JS ต่อไป เราจะได้ dictionary unpacking/destructuring ไหม?

    • อยากได้ฟีเจอร์นี้มากจริง ๆ นี่คือเหตุผลหลักที่ผมกลับไปใช้ JS
    >>> {a, b=45, c=None, **d} = {'a': 234, xzy: 32456}
    >>> print(a, b, c, d)
    234 45 None {'xyz': 32456}
    
  • แค่มีฟีเจอร์ x-string ใหม่แบบ built-in ก็รู้สึกเหมือนเป็น "ลูกเล่น" แล้ว ถ้าทำอะไรแบบนี้ได้จะเจ๋งมาก

    from foo import bar
    bar"zoop"
    
  • Zen of Python ในปี 2025:

    There should be one-- and preferably only one --obvious way to do it.
    

    การจัดรูปแบบสตริงของ Python ในปี 2025:

    • t-strings
    • f-strings
    • %-operator
    • +-operator
    • str.format()
  • ผมไม่เข้าใจว่ามันต่างจากการเอาฟังก์ชันไปใช้กับตัวแปรใน f-string ตรงไหน ดังนั้นแทนที่จะเขียนแบบนี้:

    evil = "<script>alert('bad')</script>"
    template = t"{evil}"
    safe = html(template)
    

    ทำไมไม่เขียนแบบนี้ไปเลย:

    evil = "<script>alert('bad')</script>"
    safe = f"{html(evil)}"
    

    หรือทำก่อนจะสร้าง f-string ด้วยซ้ำ มันมีไว้เพื่อกันไม่ให้ลืมขั้นตอน sanitize/จัดการสตริง และบังคับให้ต้องผ่านขั้นตอนนั้นเฉย ๆ ใช่ไหม?

  • สวัสดีครับ! ผมเป็นคนเขียนโพสต์นี้เอง :-)

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