원래 type hinting을 주제로 글을 쓰려다가, 타입 추론에 대해 글을 써내려가다 보니 이거 하나만으로도 글 하나가 대충 완성될 것 같았다. 그래서 이번에는 그냥 타입 추론 이야기를 해보려고 한다.
Python은 타입 검사가 동적이기 때문에, 모든 타입이 컴파일 타임이 아니라 런타임에 결정된다.
a = 1
b = 'planb'
c = 3.0
컴파일 타임에는 세 변수 모두 타입을 모르는 상태였다가, 코드가 실제로 실행되는 런타임에 statement들이 하나씩 해석되며 a
는 int
로, b
는 str
로, c
는 float
로 추론된다. 타입 추론이 가능한 JavaScript나 Kotlin같은 언어에서 변수의 선언문 앞에 붙이는 var
, val
, const
같은 변수 선언용 예약어가 없다는 점이 특징인데, 이 때문에 초기값 명시 없는 변수 선언이 불가능하다. 예를 들어 JavaScript의 경우, 아래와 같이 변수의 선언
과 초기화
를 두 개의 독립적인 statement로 나눌 수 있다.
var a;
a = 3;
그러나 Python은 초기값 없이 변수를 선언하는 것이 불가능하다.
a # -> 여기서 'a'가 정의되어 있지 않다는 NameError가 발생
a = 3
그래서 '변수가 선언되었으나 초기화는 되지 않았다'를 표현하기 위해, None
을 초기화 값으로 사용하곤 한다.
a = None
a = 3
사실 JavaScript도 정의만 해 둔 변수는 자동으로 초기값이 undefined
로 들어가는 것을 생각하면 이와 비슷한 맥락이기는 하다. Python은 초기값을 자동으로 넣어준다는 일이 불가능한데, 이는 a
가 변수 선언식임을 확정지을 수 없기 때문이다. 조금 더 풀어서 설명하자면,
JavaScript의 var a;
에는 var
라는 변수 선언을 위한 예약어가 있기 때문에 컴파일러가 당연히 변수 선언에 관한 instruction을 수행할테고, '초기값이 없으니 undefined로 채운다'라는 논리까지 수행하도록 언어의 스펙을 만들어낼 수 있게 된다. 초기값 명시 없는 변수 선언이 가능한 것이다. 그러나 Python의 a
는 이게 변수 선언을 의미하는 것인지, 어딘가에 선언되어 있을 a
에 접근하는 expression을 의미하는 것인지를 판단할 수 없으니, 초기값 명시 없이 변수를 선언하는 것이 불가능한 것이다.
나는 파이썬을 하기 전까진 Java를 했었는데, 파이썬을 시작하고 나서 가장 까다로웠던 게 클래스였다. 그 이유는 Java와 파이썬이 인스턴스 변수를 다루는 방식이 달라서였는데, Java의 예는 다음과 같다.
public class Person {
private String _name;
private int _age;
public Person(String name, int age) {
this._name = name;
this._age = age;
}
}
클래스에서 사용할 인스턴스 변수를 class level에 미리 정의해놓고, this
로 접근하는 방식이다. Python에서는 아래처럼 작성한다.
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
class level에 무언가를 따로 정의해두지 않았음에도 불구하고, self
에 잘 들어간다. 이는 파이썬에서 모든 객체가 dynamic attribute기 때문에 가능한 일이다.
class Tmp:
pass
t = Tmp()
t.a = 3
t.b = 4
print(t.a) # 3
굳이 자바처럼 하고 싶다면 사실
__slots__
를 사용해도 됩니다.