2.33. 인트로스펙션

몇 가지 내장 함수를 사용하면 실행 중인 프로그램이 자기 자신 – 다루고 있는 값들, 그 값들이 속한 네임스페이스, 클래스 간의 관계 – 을 들여다볼 수 있습니다. 객체가 호출자가 주장하는 것이 아니라 실제로 무엇인지 에 근거해 결정을 내려야 할 때 이들을 사용하세요.

2.33.1. 동일성과 해싱

  • 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() – 객체의 해시 값으로, dictset 이 해당 객체를 조회할 때 사용하는 바로 그 숫자입니다. 같은 두 객체는 같은 값으로 해시되며, 해시 가능한 타입(대부분 불변 값)만이 동작합니다.

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

2.33.2. 타입과 호출 가능성 조회하기

  • type() – 값의 정확한 클래스입니다. type(x) is int 는 “x가 정확히 int인가”(하위 클래스는 제외)를 묻습니다; 보통 원하는 것은 isinstance() 쪽입니다.

  • isinstance() – “x가 이 클래스의 인스턴스이거나 그 하위 클래스의 인스턴스인가?” 함수 내부에서 타입 기반 분기를 할 때 쓰는 표준 도구입니다.

  • issubclass() – 클래스 수준의 대응 함수입니다. 인스턴스가 아니라 두 개의 클래스를 받습니다.

  • 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. 범위 들여다보기

  • globals() – 모듈의 전역 네임스페이스를 dict 로 반환합니다. 여기서 읽는 것은 잘 동작하고, 쓰는 것도 실제로 반영되지만, REPL 탐색 외의 상황에서 그렇게 하면 프로그램을 따라가기 어려워집니다.

  • locals() – 호출 지점의 지역 네임스페이스입니다. 함수 내부에서는 지역 변수를 반영하지만, 반환된 딕셔너리를 수정 하는 것이 실제 지역 변수에 다시 기록된다고 보장되지는 않습니다(구현에 따라 정의되는 동작).

name = "OpenMV"

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

이 둘은 디버깅과, 무엇이 정의되어 있는지 발견해야 하는 도구에 유용합니다. 일반 코드에서는 아껴서 사용하세요 – globals() 를 변형하는 함수는 Python에서 가장 추론하기 어려운 것 중 하나입니다.

2.33.4. 참고

디버깅 에서 다루는 dir()help() 는 알 수 없는 객체의 표면을 탐색하는 일상적인 인트로스펙션 도구입니다.