본문 바로가기

진리는어디에/Python

[Python] 변수 #2 변수의 타입

이번 포스트에는 파이썬의 타입들에 대해 살펴 보도록 한다. 간단한 내용들이므로 가볍게 읽고 다음으로 넘어가도록 하자.
  1. 파이썬 변수의 소개
  2. >> 변수의 타입
  3. 변수의 다양한 정보 확인
  4. ctype 모듈을 활용한 변수의 정보 확인
  5. == 와 is 연산자
  6. mutable 변수와 immutable 변수
  7. 변수의 삭제
  8. 파이썬 정수는 Overflow가 없다?

파이썬 표준 타입 종류

아래는 파이썬에서 제공하고 있는 표준 타입들을 나열했다.

  • 정수형 : int
  • 실수형 : float
  • 문자열 : str
  • 시퀀스(Sequence) : list, tuple, set, dictionary, ...
  • 기타 :  bool, NoneType, range, slice, ...

정수형 데이터를 저장하기 위해 int, 실수형 데이터를 위한 float, 문자열을 위한 str. 그 외에 시퀀스와 기타등등 다양한 타입들을 제공한다. 기타 타입들에 대해서는 나올때 마다 설명을 하도록하고, 시퀀스 타입들에 대해서는 따로 설명하는 장을 만들도록 하겠다. 이번 포스트에서는 파이썬의 표준 타입 중 정수형, 실수형, 문자열 타입에 집중하도록 한다.

아래 int타입의 변수를 생성하는 다양한 방법들을 보자.

n1 = 10			# 단순히 정수 리터럴을 이용해 생성 가능
n2 = int(10)		# 데이터 타입과 초기값을 지정해도 됨
n3 = int()		# 데이터 타입만으로도 생성 가능. 초기값 0으로 채워진다
n4 = int('10')		# 정수로 변환 가능한 문자열로 부터도 생성 가능
n5 = int('ABCD') 	# value error

마치 int라는 함수를 호출하는 것 같아 보이지만 int 타입의 객체를 생성하는 코드들이다. [Python] 파이썬 기초부터 시작하기 - Overview 포스트에서 클래스를 생성하던 방법을 기억하라.

C/C++에서 primitive 타입을 제공하는 것과는 다르게, 파이썬의 모든 것들은 객체로 이루어져 있다. int 타입이든, float 타입이든 결국 이것 또한 객체의 한 종류다.

파이썬에서의 객체

변수 타입 이야기 하다가 갑자기 객체가 나와서 뜬금 없을 수도 있지만 파이썬 타입에 대해 이야기 전에 반드시 객체에 대해 이야기를 하고 넘어가야 한다. 왜냐면 파이썬의 모든 것들은 '객체'로 구성되어 있고 파이썬의 '타입'이라는 것도 결국 객체의 일부분이기 때문이다.

예를 들어 우리가 int타입 변수를 하나 생성한다고 가정하면, C/C++에서는 primitive 타입이라고 아무런 멥버함수를 가지지 않은 값만 들고 있는 변수를 생성하지만, 파이썬에서는 엄현히 메소드와 데이터를 가진 객체를 생성한다.

예를 들어 객체가 가진 모든 메소드를 출력하는 dir 표준함수를 이용하여 int형 객체나 타입을 조사해보면 아래와 같이 사용가능한 메소드들이 출력 되는 것을 볼수 있다.

>>> dir(int)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> n1 = 10
>>> dir(n1)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

이런 파이썬의 객체들은 모두 object라는 최상위 객체로 부터 파생 되었다. 파이썬의 상속 계층을 조사할 수 있는 issubclass(derived, base) 함수를 이용해 int 타입을 object 의 하위 타입인지 조사해보면 True가 반환 되는 것을 볼 수 있다.

상속 계층 조사

>>> issubclass(int, object)
True
>>> int.mro()
[<class 'int'>, <class 'object'>]
  • issubclass(derived, base) : derived 객체가 base 타입의 하위 클래스 여부인지 조사
  • 타입이름.mro() : 타입의 상속 계층을 출력

타입도 객체다

[Python] 파이썬 기초부터 시작하기 - 변수에서 살펴본 파이썬 변수의 메모리 구조를 떠올려 보자.

n1 = 10
n2 = n1
n3 = 20

위 예제 코드는 아래와 같은 메모리 형태를 가진다.

n1, n2, n3가 가리키는 객체들의 ob_type이 공통적으로 타입을 정의하는객체를 가리키고 있다. 이 경우에는 int형 변수이므로 int 타입 정보를 가진 객체를 가리키고 있을 것이다.

그렇다면 타입 정보는 무엇일까? 파이썬에서는 타입 정보 또한 하나의 객체이다. 위의 그림을 좀 더 자세히 살펴 보면 아래와 같다.

int 타입 객체 또한 다른 객체들 처럼 ob_refcnt와 ob_type을 기본적으로 가지고 있고 그 외에 타입을 나타내기 위한 정보들을 가진 객체일 뿐이다.

아래 예제는 변수 n1이 가리키는 객체와 int 타입을 나타내는 객체의 주소를 출력한다.

n1 = 10

print(hex(id(n1)))
print(hex(id(int))) # 타입 객체의 주소

여기서 한가지 재밌는 파이썬 만의 특징이 있다. 우리가 다른 일반적인 프로그래밍 언어에서 int라고 하는 것은 데이터 타입을 나타내는 예약 키워드지만 파이썬에서 int는 단지 타입을 나타내는 객체를 가리키고 있는 변수일 뿐이다.

Tip. Testing for Python keywords 

keyword.kwlist 를 통해서 파이썬이 예약한 키워드 리스트를 확인 할 수 있다.

>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', '__peg_parser__', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

아래 처럼 int를 가지고 몇가지 테스트를 해보도록 하자.

DWORD = int # int는 키워드가 아니라 타입 객체를 가리키는 변수
n1 = int # 지금 부터 n1은 타입 객체를 가리키는 변수가 된다

n3 = DWORD(10) # DWORD는 타입 객체를 가리키는 변수이므로 n3는 10을 가리키는 변수가 된다
n4 = n1(10)

int = n4 # int는 단순 변수이기에 다른 값으로 덮어 쓸 수 있다.
n5 = int(10) # error, int는 10이 되었으므로 더 이상 타입으로 취급 받지 못한다

1~2 라인에서 int가 가리키는 타입 객체를 DWORD, n1 변수도 같이 가리키게 했다. 4~5 라인에서 DWORD와 n1은 int 타입 처럼 사용하는 것을 볼 수 있다.

7 라인에서 int에 n4. 즉, 10을 대입했다. int는 타입 객체를 가리키고 있지 않으므로 더이상 타입으로써의 역할을 하지 못하고 8 라인에서 int를 통해 정수 변수를 생성하고자하면 호출 가능한 객체가 아니라는 오류를 발생시킨다.

함수의 인자와 리턴 값으로 타입을 사용할 수 있다

def foo(x) :
    return float
    
FLOAT = foo(int)

f = FLOAT(10.1)

type() 함수

type() 함수는 변수가 가리키고 있는 객체의 타입 객체를 리턴하는 함수다.

n1 = int(10)

print(hex(id(type(n1))))
print(hex(id(int)))

위 코드의 실행 결과가 같은 것을 볼 수 있다. type함수를 이용해 정수형인 변수 n1을 조사하면 int 타입 객체와 동일한 주소가 리턴된다.

마치며

이상 파이썬의 타입에 대해 알아 보았다. 중요 사항만 다시 짚어보면

  • 파이썬에선 타입 또한 하나의 객체다
  • int, float는 예약어가 아니라 타입 객체를 가리키고 있는 변수일 뿐이다.
  • type 표준 함수를 통해 객체가 가리키고 있는 타입 객체를 리턴 받을 수 있다.

정도로 정리 될 수 있겠다.

타입 객체를 가리키는 int, float 변수들을 다른 객체를 가리키도록 재할당 하므로써 엄청난 유연성을 가질수 있지만, 반면에 원하지 않는 사이드이펙트를 발생 시킬 수도 있다. 타입 객체들을 가리키는 변수들을 손댈때는 조심하도록 하자.

다음글 : 변수의 다양한 정보 확인

부록 1. 같이 보면 좋은 글

 

 

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!