2.33. การตรวจสอบตัวเอง (Introspection)

built-in เพียงไม่กี่ตัวช่วยให้โปรแกรมที่กำลังทำงานตรวจสอบตัวเองได้ ไม่ว่าจะเป็นค่าที่กำลังทำงานด้วย, namespace ที่ค่าเหล่านั้นอาศัยอยู่ และความสัมพันธ์ระหว่างคลาส ใช้พวกมันเมื่อต้องการตัดสินใจตามสิ่งที่ออบเจกต์ เป็นจริงๆ ไม่ใช่สิ่งที่ผู้เรียกอ้างว่ามันเป็น

2.33.1. Identity และการ hash

  • id() -- จำนวนเต็มเฉพาะที่ระบุออบเจกต์ตลอดที่มันยังมีชีวิตอยู่ ชื่อสองชื่อที่ผูกกับออบเจกต์เดียวกันจะคืนค่า id เดียวกัน ออบเจกต์สองตัวที่เท่ากันแต่ต่างกันจะไม่คืนค่า id เดียวกัน

>>> a = [1, 2, 3]
>>> b = a
>>> c = [1, 2, 3]
>>> id(a) == id(b)        # same list
True
>>> id(a) == id(c)        # equal but distinct
False

id ไม่สามารถส่งต่อระหว่างการรันและไม่มีความหมายเกินกว่า "ออบเจกต์เดียวกันหรือต่างกัน"

  • hash() -- hash value ของออบเจกต์ ตัวเลขเดียวกับที่ dict และ set ใช้ค้นหา ออบเจกต์สองตัวที่เท่ากันจะ hash เป็นค่าเดียวกัน เฉพาะ type ที่ hashable ได้ (ส่วนใหญ่คือค่า immutable) เท่านั้นที่ทำงานได้เลย

>>> hash("abc")           # some integer, build-dependent
-1600925533
>>> hash([1, 2])
TypeError: unhashable type: 'list'

2.33.2. การ query ประเภทและความสามารถเรียกได้

  • type() -- คลาสที่แน่นอนของค่า type(x) is int ถามว่า "x เป็น int พอดีหรือไม่" (ไม่รวม subclass) ปกติแล้ว isinstance() คือสิ่งที่คุณต้องการแทน

  • isinstance() -- "x เป็น instance ของคลาสนี้หรือ subclass ของมันหรือไม่?" เป็นเครื่องมือมาตรฐานสำหรับ dispatch ตาม type ภายในฟังก์ชัน

  • issubclass() -- counterpart ระดับคลาส รับสองคลาสแทนที่จะเป็น instance

  • callable() -- True หากอาร์กิวเมนต์สามารถเรียกด้วย () ได้ มีประโยชน์เมื่อคุณรับอาร์กิวเมนต์ที่อาจเป็นฟังก์ชันหรืออาจเป็นค่าธรรมดา

>>> isinstance(3, int)
True
>>> isinstance(True, int)        # bool is a subclass of int
True
>>> issubclass(bool, int)
True
>>> callable(len)
True
>>> callable(10)
False

รูปแบบที่ใช้ callable:

def call_or_return(x):
    return x() if callable(x) else x

2.33.3. การดู scope

  • globals() -- global namespace ของ module เป็น dict การอ่านจากมันทำงานได้ การเขียนมันก็จริงได้เช่นกัน แต่การทำเช่นนั้นนอกจาก REPL exploration จะทำให้โปรแกรมยากต่อการติดตาม

  • locals() -- local namespace ที่ call site ภายในฟังก์ชันมันสะท้อน local variable; การแก้ไข dict ที่คืนค่ามาไม่ได้รับประกันว่าจะเขียนกลับไปยัง local จริงๆ (พฤติกรรมที่ขึ้นอยู่กับ implementation)

name = "OpenMV"

def f():
    x = 10
    print(globals()["name"])    # OpenMV
    print(locals())             # {'x': 10}

ทั้งสองมีประโยชน์สำหรับการ debug และสำหรับเครื่องมือที่ต้องค้นพบว่ามีอะไรถูกนิยามไว้ ใช้พวกมันอย่างประหยัดในโค้ดปกติ ฟังก์ชันที่ mutate globals() เป็นหนึ่งในสิ่งที่ยากที่สุดในการให้เหตุผลใน Python

2.33.4. ดูเพิ่มเติม

dir() และ help() ที่ครอบคลุมใน debugging คือเครื่องมือ introspection ที่ใช้ประจำวันสำหรับสำรวจพื้นผิวของออบเจกต์ที่ไม่รู้จัก