งูขดต้นฉบับกำลังส่องตู้วัตถุผ่านกระจกนิ่งโดยไม่แตะคันโยก property ด้านข้าง

Python inspect.getmembers_static: สำรวจ object โดยไม่เผลอเรียก property

พี่โชว์Engineering

เครื่องมือสำรวจ object อาจเผลอรัน property, descriptor หรือ __getattr__ แค่เพราะต้องการรายชื่อสมาชิกครับ inspect.getmembers_static หลีกเลี่ยง dynamic lookup จึงเหมาะกับ debugger, linter และ security tooling ที่ไม่ควรเปลี่ยน state ของสิ่งที่กำลังตรวจ

import inspect
import unittest


class Meter:
    def __init__(self):
        self.reads = 0

    @property
    def secret(self):
        self.reads += 1
        return "value"


class InspectTests(unittest.TestCase):
    def test_static_inspection_does_not_execute_property(self):
        meter = Meter()
        members = dict(inspect.getmembers_static(meter))
        self.assertIsInstance(members["secret"], property)
        self.assertEqual(meter.reads, 0)

        dynamic = dict(inspect.getmembers(meter))
        self.assertEqual(dynamic["secret"], "value")
        self.assertEqual(meter.reads, 1)


if __name__ == "__main__":
    unittest.main()

Static ไม่ได้แปลว่าครบเหมือน runtime

วิธี static อาจคืน descriptor object แทนค่าที่ผู้ใช้เห็น และอาจเห็นสมาชิกบางแบบไม่เหมือน dynamic lookup นี่เป็น feature ไม่ใช่ bug เลือก API ตามคำถาม: ต้องการโครงสร้างที่ประกาศไว้ หรือค่าหลัง Python เรียก protocol แล้ว

เหมือนตรวจแผงไฟจากแบบวงจรแทนการกดทุกสวิตช์ เราปลอดภัยกว่า แต่ยังไม่ได้พิสูจน์ว่าอุปกรณ์ปลายทางทำงานจริง

Recap

  • ใช้ static inspection เมื่อ side effect ยอมรับไม่ได้
  • คาดว่า descriptor จะยังไม่ถูก bind
  • อย่าอ้างว่ารายการ static เท่ากับ runtime view ทุกกรณี
  • ทดสอบกับ metaclass/descriptor ของระบบจริงก่อนทำ tooling