2.33. Introspekcja

Garść funkcji wbudowanych pozwala działającemu programowi badać samego siebie – wartości, na których pracuje, przestrzenie nazw, w których te wartości żyją, oraz relacje między klasami. Sięgaj po nie, gdy musisz podejmować decyzje na podstawie tego, czym obiekt naprawdę jest, a nie tego, za co podaje go jego wywołujący.

2.33.1. Tożsamość i haszowanie

  • id() – unikalna liczba całkowita, która identyfikuje obiekt tak długo, jak długo on istnieje. Dwie nazwy wiążące ten sam obiekt zwracają to samo id; dwa równe, ale odrębne obiekty – nie.

>>> 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 nie jest przenośne między uruchomieniami i nie ma znaczenia poza odpowiedzią na pytanie „ten sam obiekt czy inny obiekt”.

  • hash() – wartość skrótu obiektu, ta sama liczba, której dict i set używają do jego wyszukiwania. Dwa równe obiekty haszują się do tej samej wartości; w ogóle działają tylko typy haszowalne (przeważnie wartości niezmienne).

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

2.33.2. Sprawdzanie typów i wywoływalności

  • type() – dokładna klasa wartości. type(x) is int pyta „czy x jest dokładnie typem int” (bez podklas); isinstance() jest zwykle tym, czego naprawdę chcesz.

  • isinstance() – „czy x jest instancją tej klasy lub jej podklasy?” Standardowe narzędzie do rozgałęziania działania na podstawie typu wewnątrz funkcji.

  • issubclass() – odpowiednik na poziomie klas. Przyjmuje dwie klasy zamiast instancji.

  • callable()True, jeśli argument można wywołać za pomocą (). Przydatne, gdy otrzymujesz argument, który może być funkcją albo zwykłą wartością.

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

Wzorzec wykorzystujący callable:

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

2.33.3. Przyglądanie się zakresom

  • globals() – globalna przestrzeń nazw modułu jako dict. Odczyt z niej działa; zapis do niej jest realny, ale robienie tego poza eksploracją w REPL utrudnia śledzenie programu.

  • locals() – lokalna przestrzeń nazw w miejscu wywołania. Wewnątrz funkcji odzwierciedla zmienne lokalne; modyfikowanie zwróconego słownika nie gwarantuje zapisu z powrotem do rzeczywistych zmiennych lokalnych (zachowanie zależne od implementacji).

name = "OpenMV"

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

Te dwie funkcje są przydatne do debugowania oraz dla narzędzi, które muszą odkryć, co zostało zdefiniowane. Sięgaj po nie oszczędnie w zwykłym kodzie – funkcja, która modyfikuje globals(), jest jedną z najtrudniejszych do zrozumienia rzeczy w Pythonie.

2.33.4. Zobacz także

dir() i help(), omówione w debugging, to codzienne narzędzia introspekcji do badania powierzchni nieznanego obiektu.