พรีโปรเซสเซอร์ของ Python
(pydong.org)พรีโปรเซสเซอร์ของ Python
- ข้ออ้างที่ว่า Python ไม่มีพรีโปรเซสเซอร์นั้นไม่เป็นความจริง
- Python มีพรีโปรเซสเซอร์ที่ทรงพลังมาก
การเข้ารหัสซอร์สโค้ดของ Python
- ด้วย PEP-0263 จึงสามารถกำหนดการเข้ารหัสของซอร์สโค้ดได้
- สามารถตั้งค่าการเข้ารหัสได้โดยเพิ่ม magic comment ไว้ในสองบรรทัดแรก
- ตัวอย่าง:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
ไฟล์กำหนดค่า path (.pth)
- เมื่อ Python interpreter เริ่มทำงานโดยไม่มีออปชัน
-Sจะโหลดแพ็กเกจsiteโดยอัตโนมัติ - สามารถเพิ่มไฟล์
.pthในโฟลเดอร์site-packagesเพื่อขยายเส้นทางค้นหาโมดูลได้ - บรรทัดที่ขึ้นต้นด้วย
importในไฟล์.pthจะถูกนำไปทำงาน - ทำให้สามารถรันโค้ดใดก็ได้ระหว่างการเริ่มต้นทำงานของ Python interpreter
การกำหนด codec แบบกำหนดเอง
- มีความต้องการสองอย่างที่ Python interpreter ต้องพอใจ:
- ฟังก์ชัน
decode(data: bytes) -> tuple[str, int] - คลาส incremental decoder
- ฟังก์ชัน
- ใช้
codecs.utf_8_decodeเพื่อทำการถอดรหัสจริง จากนั้นส่งสตริงผลลัพธ์ไปยังพรีโปรเซสเซอร์ - ควรจับ exception แล้วพิมพ์ออกมาก่อนโยนซ้ำอีกครั้ง
การจัดเตรียม incremental decoder
- สืบทอดจาก
codecs.BufferedIncrementalDecoderเพื่อทำ incremental decoder - รวบรวมข้อมูลไว้ในบัฟเฟอร์ แล้วพรีโปรเซสทั้งไฟล์เมื่อมีการเรียกถอดรหัสครั้งสุดท้าย
การขยาย Python
- การขยาย Python โดยใช้ standard library ของ Python นั้นค่อนข้างง่าย
- สามารถใช้โมดูล
tokenizeเพื่อแก้ไข token stream ของไฟล์ หรือใช้โมดูลastเพื่อแก้ไข abstract syntax tree ได้
การเพิ่มและลดค่าแบบ unary
- Python ไม่มีตัวดำเนินการเพิ่มและลดค่าแบบ unary
x++,x--ใช้ไม่ได้++x,--xใช้ได้ แต่มีความหมายต่างออกไป- สามารถแปลงนิพจน์เพิ่มและลดค่าแบบ unary ให้เป็นนิพจน์ Python ได้
ตัวอย่าง
- ไฟล์อินพุต
incdec.py:# coding: magic.incdec i = 6 assert i-- == 6 assert i == 5 assert ++i == 6 assert --i == 5 assert i++ == 5 assert i == 6 assert (++i, 'i++') == (7, 'i++') print("PASSED") - ไฟล์ที่แปลงแล้ว:
i = 6 assert ((i, i := i - 1)[0]) == 6 assert i == 5 assert ((i, i := i + 1)[1]) == 6 assert ((i, i := i - 1)[1]) == 5 assert ((i, i := i + 1)[0]) == 5 assert i == 6 assert (((i, i := i + 1)[1]), 'i++') == (7, 'i++') print("PASSED")
Python ที่ใช้วงเล็บปีกกา (Bython)
- สามารถใช้วงเล็บปีกกาเพื่อกำหนดขอบเขตแทนการเยื้องของ Python ได้
- ใช้
tokenize.generate_tokensเพื่อแก้ไข token stream
ตัวอย่าง
- ไฟล์อินพุต
test.by:# coding: magic.braces def print_message(num_of_times) { for i in range(num_of_times) { print("braces ftw") } print({'x': 3}) } x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__" { print_message(2) print({k: v for k, v in x.items()}) } - ไฟล์ที่แปลงแล้ว:
def print_message(num_of_times): for i in range(num_of_times): print("braces ftw") print({'x': 3}) x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__": print_message(2) print({k: v for k, v in x.items()})
การตีความภาษาอื่น
- สามารถทำให้ Python interpreter ตีความภาษาอื่นได้
- เช่น C, C++, TOML
ตัวอย่าง
- ไฟล์ C++
test.cpp:#define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } - ไฟล์ที่แปลงแล้ว:
import cppyy cppyy.cppdef(r""" #define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } """) from cppyy.gbl import main if __name__ == "__main__": main()
การตรวจสอบความถูกต้องของข้อมูล
- สามารถตรวจสอบข้อมูลในรูปแบบ TOML ด้วย JSON Schema ได้
- ใช้
jsonschemaเพื่อทำการตรวจสอบจริง
ตัวอย่าง
- ไฟล์สคีมา
schema.json:{ "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "number"}, "scores": { "type": "array", "items": {"type": "number"} }, "address": {"$ref": "#/$defs/address"} }, "required": ["name"], "$defs": { "address": { "type": "object", "properties": { "street": {"type": "string"}, "postcode": {"type": "number"} }, "required": ["street"] } } } - ไฟล์ข้อมูลที่ถูกต้อง
data_valid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, 20, 80, 90] [address] street = "Grove St. 4" postcode = 19201 - ไฟล์ข้อมูลที่ไม่ถูกต้อง
data_invalid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, "20", 80, 90] [address] street = "Grove St. 4" postcode = 19201
บทสรุป
- สามารถใช้ codec แบบกำหนดเองและไฟล์กำหนดค่า path เพื่อเปลี่ยนพฤติกรรมของ Python interpreter ได้อย่างมาก
- ตัวอย่างได้แก่
pythonql,future-typing,future-fstrings,future-annotations - สามารถใช้
magic_codecเพื่อทดลองพรีโปรเซสเซอร์ของตนเองได้อย่างง่ายดาย
สรุปโดย GN⁺
- สามารถใช้พรีโปรเซสเซอร์ของ Python เพื่อทำส่วนขยายภาษาหลากหลายแบบและตรวจสอบความถูกต้องของข้อมูลได้
- อธิบายวิธีเปลี่ยนพฤติกรรมของ Python interpreter ผ่าน codec แบบกำหนดเอง
- บทความนี้มอบเครื่องมือและเทคนิคที่เป็นประโยชน์ให้กับนักพัฒนา Python
- โปรเจ็กต์ที่มีความสามารถคล้ายกัน ได้แก่
pythonql,future-typingเป็นต้น
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ข้อความแสดงข้อผิดพลาดทางไวยากรณ์ของ
from __future__ import bracesถูกฮาร์ดโค้ดไว้ใน cpython มาตั้งแต่ปี 2001เคยคิดถึงวิธีไล่คนออกแบบสร้างสรรค์ด้วย import-hooks แต่ก็น่าเสียดายที่ codec regex กันไม่ให้ใช้สิ่งอย่าง
μtf8sys.settraceมาใช้ monkey patch ทุกฟังก์ชันให้กลายเป็นฟังก์ชันที่ถูกเรียกก่อนหน้า และสลับ stdout กับ stderr ทุก ๆ 17 นาทีมีเหตุผลที่ Python ไม่เปิดเผย preprocessor hook และคิดว่าผู้ใหญ่ที่มีเหตุผลควรหลีกเลี่ยงมัน
preprocessor สะดวกและมีประโยชน์กว่า
exit()ชอบความยืดหยุ่นของ Python
กรณีใช้งานที่ดีที่สุดของ pyxl ได้แรงบันดาลใจจาก jsx
# coding: pyxlสงสัยว่าการเปลี่ยนผ่านจาก Python 2 ไป 3 จะจัดการได้ดีกว่านี้หรือไม่
# coding: six.python2สามารถทำให้โค้ด Python2 ใช้ได้อย่างถูกต้องใน Python3 และ# coding: six.python3ก็สามารถปรับโค้ด Python3 ให้รันได้ใน Python2ดีใจที่มีคนชอบไอเดียนี้ และจะมีเนื้อหาเพิ่มเติมตามมาเร็ว ๆ นี้
รู้สึกทึ่งกับไอเดียใหม่อย่างแท้จริงหลังจากไม่ได้เจอมานาน
ถ้าต้องการการสร้างโค้ดแบบ inline ใน Python สามารถใช้ cog ของ Ned Batchelder ได้
สงสัยว่า dependency ที่ถูกนำเข้ามาด้วยกลยุทธ์ coding hook นี้จะถูกตรวจจับโดย
pip freezeหรือ uv หรือไม่