정적 타입과 동적 타입 비교 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

# 34_static-vs-dynamic-typing_verify.py
# 정적 타입과 동적 타입 비교 문서의 예를 확인한다. 문서의 a = 3; a = "ok"; 를
# (가) 동적 타입 언어인 파이썬에서 실제로 돌리고, (나) 선언된 타입을 미리 보는 작은 정적 검사기에 넣는다.
# 표준 라이브러리만 쓴다.
# 실행: python3 34_static-vs-dynamic-typing_verify.py


def static_check(program):
    """program: ("decl", 타입, 이름, 값) 또는 ("assign", 이름, 값)의 목록.
    실행하지 않고 선언된 타입만 보고 틀린 줄 번호들을 돌려준다."""
    kind = {int: "int", str: "string", bool: "bool", float: "double"}
    declared, errors = {}, []
    for no, stmt in enumerate(program, 1):
        if stmt[0] == "decl":
            declared[stmt[2]] = stmt[1]
            if kind[type(stmt[3])] != stmt[1]:
                errors.append(no)
        else:
            if stmt[1] not in declared or kind[type(stmt[2])] != declared[stmt[1]]:
                errors.append(no)
    return errors


def risky(flag):
    if flag:
        return 1 + "a"        # 이 줄에 닿아야 타입 오류가 난다
    return 0


if __name__ == "__main__":
    # (가) 동적 타입: 타입은 변수가 아니라 값에 붙는다. 같은 변수에 다른 타입의 값을 넣어도 된다
    a = 3
    assert type(a) is int
    a = "ok"
    assert type(a) is str

    # 검사는 실행 중에, 그 연산에 닿았을 때 한다
    assert risky(False) == 0                 # 틀린 줄이 있어도 그 줄을 지나지 않으면 돈다
    try:
        risky(True)
        raised = False
    except TypeError:
        raised = True
    assert raised

    # (나) 정적 타입: int a = 3; a = "ok";  실행하기 전에 둘째 줄에서 걸린다
    assert static_check([("decl", "int", "a", 3), ("assign", "a", "ok")]) == [2]
    assert static_check([("decl", "int", "a", 3), ("assign", "a", 4)]) == []
    # 지나가지 않는 줄의 오류도 잡는다(실행 경로와 상관없이 모든 줄을 본다)
    assert static_check([("decl", "int", "a", 3), ("decl", "bool", "f", False),
                         ("assign", "a", "never run")]) == [3]
    print("ALL CHECKS PASSED")