여기 Python list가 하나 있다.
array = list(range(10000))
Python을 아는 사람이라면, array
의 길이를 얻는 방법은 다음과 같이 간단하다는 것을 알 것이다.
length_or_array = len(array) # == 10000
C++이나 Java 등의 다른 객체형 언어를 먼저 알고 있었다면 이런 의문이 들 수 있다.
"array.size
또는 array.length
같이 안 하고 왜 len
이라는 함수를 쓰지?"
질문에 대한 답을 하기 전에 먼저 코드 몇 뭉치를 보고 오는 게 더 좋을 것 같다.
1. len
은 __len__
을 호출한다.
class ClassWithLen:
def __init__(self, *items):
self.storage = list(items)
def __len__(self):
return len(self.storage)
>>> obj = ClassWithLen(1, 2, 3, 4)
>>> len(obj)
4
너무 뻔한가? 하지만 len
이 object.__len__
과 같다고 생각하면 안된다.
class ClassWithLen:
def __len__(self):
return 'Really lengthy length!'
>>> obj = ClassWithLen(1, 2, 3, 4)
>>> obj.__len__()
'Really lengthy length!'
>>> len(obj)
TypeError: 'str' object cannot be interpreted as an integer
Python이 제공하는 len
함수는 class instance의 __len__
method가 만들어져 있는지부터 체크하고, 그 return 유형과 값이 알맞는 지까지 검사해서 최종적으로 0 이상의 정수 값을 주게 된다.
2. __iter__
없이 iteration이 돌아갈 수 있다.
class ClassIterable:
def __init__(self, *items):
self.storage = list(items)
def __getitem__(self, idx):
return self.storage[idx]
>>> obj = ClassIterable(1, 2, 3)
>>> for item in obj: print(item)
1
2
3
Python이 어느 obj를 통해 for loop을 돌고자 할 때 다음과 같은 순서로 체크한다.
__iter__
가 제공되는지 체크하고, 있으면 이를 통해iter(obj)
를 사용하는 것으로 끝난다.- 없다면
__getitem__
이 있는지 체크하고, 있으면 index 0부터 시작해서IndexError
가 나올 때까지__getitem__(index)
를 호출해서 for loop을 제공한다.
이러한 일종의 규약 (protocol) 덕분에 __getitem__
하나만 있으면 iterable type이 되고, 추가 코드 없이 아래와 같은 것들이 가능하다.
>>> obj[-1] # negative index 가능
3
>>> obj[1:] # slicing 가능
[2, 3]
>>> list(obj) # iterable로 인자를 받는 모든 곳에 적용 가능
[1, 2, 3]
>>> [item for item in obj] # listcomp, genexp 가능
[1, 2, 3]
>>> 1 in obj, 5 in obj # __contains__ 없이 가능
(True, False)
Duck Typing
지금까지 써 본 예제를 통해 알 수 있는 Python data type의 특징이 바로 "Duck Typing"이다.
If it walks like a duck and it quacks like a duck, then it must be a duck.
"duck"이라는 이름에 구애받지 않고 "duck"이라는 종류에 맞는 행동을 하는 가를 구분의 기준으로 본다는 것이다. 그리고 Python은 그 "행동"을 magic method (또는 special method)라는 이름으로 칭하고 수 많은 행동을 정해놓고, 계속 개정되며 추가되고 있다.
대표적인 Python data type으로는 iterable, callable, awaitable... 또 뭐가 있더라, file-like object나 bytes-like object 등의 표현도 duck typing의 철학을 담고 있다고 볼 수 있다.
또한 Python에서 그 interface는 강제되지 않는다. 위의 __getitem__
을 통한 iterable typing은 그 대표적인 예일텐데, 이와 같이 코드 레벨에서 강제되지 않고, 일종의 규약으로서 문서 레벨에서 제공되는 interface라고 이야기할 수 있다. 이를 OOP에서 protocol이라고도 부른다고 한다. Iterable type을 만족하기 위해 꼭 iterator class가 있어야만 하는 것도 아니고, __iter__
가 마련되어 있어야만 하는 것이 아니다. 굉장히 유연한 data type model을 갖고 있는데, dynamic language로서의 특징을 살리고자 하는 BDFL의 설계라고 볼 수 있을 것이다.
Python의 type system에 대한 컨셉을 len
함수에서 볼 수 있었다.
참고 자료
Fluent Python, Luciano Ramalho, O'Reilly
강추하는 Python 책
관련 요약글 dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/
wikipedia.org
Duck Typing wikipedia.org/wiki/Duck_typing
Protocol wikipedia.org/wiki/Protocol_(object-oriented_programming)
python.org
Data model - special methods docs.python.org/3/reference/datamodel.html#special-method-names
Glossary docs.python.org/3/glossary.html
'Programming > Python' 카테고리의 다른 글
왜 Python에는 GIL이 있는가 (16) | 2018.06.02 |
---|---|
Python 계의 npm, Pipenv 첫 사용 후기 (3) | 2017.12.21 |
django admin에서 ForeignKey 선택 제한 (0) | 2017.02.18 |
python json 모듈 사용시 not JSON serializable 에러를 피하는 방법 (0) | 2016.12.12 |
Python Postgresql adapter, psycopg2 설치 (Linux/Windows) (0) | 2016.11.02 |