2.33. Introspectie

Een handvol ingebouwde functies laat een draaiend programma zichzelf inspecteren – de waarden waarmee het werkt, de namespaces waarin die waarden leven, en de relaties tussen klassen. Grijp ernaar wanneer je beslissingen moet nemen op basis van wat een object daadwerkelijk is, in plaats van wat de aanroeper ervan beweert.

2.33.1. Identiteit en hashing

  • id() – een uniek geheel getal dat een object identificeert zolang het in leven is. Twee namen die hetzelfde object binden, retourneren dezelfde id; twee gelijke maar verschillende objecten niet.

>>> 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

De id is niet draagbaar tussen runs en niet betekenisvol verder dan “hetzelfde object of een ander object.”

  • hash() – de hashwaarde van een object, hetzelfde getal dat dict en set gebruiken om het op te zoeken. Twee gelijke objecten hashen naar dezelfde waarde; alleen hashbare types (onveranderlijke waarden, meestal) werken überhaupt.

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

2.33.2. Types en aanroepbaarheid opvragen

  • type() – de exacte klasse van een waarde. type(x) is int vraagt “is x exact een int” (geen subklassen); isinstance() is meestal wat je in plaats daarvan wilt.

  • isinstance() – “is x een instantie van deze klasse, of een subklasse ervan?” Het standaardgereedschap voor type-gebaseerde dispatch binnen functies.

  • issubclass() – de tegenhanger op klasseniveau. Neemt twee klassen in plaats van een instantie.

  • callable()True als het argument met () kan worden aangeroepen. Handig wanneer je een argument ontvangt dat een functie kan zijn of een gewone waarde.

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

Een patroon dat callable gebruikt:

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

2.33.3. Naar scopes kijken

  • globals() – de globale namespace van de module als een dict. Eruit lezen werkt; ernaar schrijven is echt, maar dit doen buiten REPL-verkenning maakt een programma moeilijk te volgen.

  • locals() – de lokale namespace op de aanroeplocatie. Binnen een functie weerspiegelt het de lokale variabelen; het aanpassen van de geretourneerde dict schrijft niet gegarandeerd terug naar de werkelijke locals (implementatie-afhankelijk gedrag).

name = "OpenMV"

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

Deze twee zijn nuttig voor debuggen en voor gereedschappen die moeten ontdekken wat er is gedefinieerd. Grijp er spaarzaam naar in gewone code – een functie die globals() muteert, is een van de moeilijkste dingen om over te redeneren in Python.

2.33.4. Zie ook

dir() en help(), behandeld in debugging, zijn de alledaagse introspectiegereedschappen om het oppervlak van een onbekend object te verkennen.