[파이썬] 스터디 시작하기
#Programming Language#Python

[파이썬] 스터디 시작하기

스터디 시작하기

파이썬 스터디를 시작합니다! 저는 주요 교재로 다음의 두 권을 사용합니다.

  • Luciano Ramalho, Fluent Python, 2nd Edition, O’Reilly, 2022
  • Brett Slatkin, Effective Python, Second Edition, Pearson, 2020

무엇을 다루나요?

이 스터디에서는 파이썬을 능숙하게 다루는 법을 배웁니다.

많은 사람들이 파이썬의 기본 문법을 배우고 사용하지만, 파이썬을 파이썬답게 다루는 사람은 드뭅니다. 저 역시 그렇습니다. 그래서 파이썬의 다양한 측면을 더 잘 이해하고 활용하기 위해 스터디를 준비했습니다. 이 스터디를 통해 우리 모두 파이써니스타가 되어 봅시다!

'파이써닉'한 코드

'파이써닉'이라는 용어는 파이썬의 독특한 특성과 관행을 효율적으로 활용하는 코딩 스타일을 의미합니다. 이러한 스타일은 코드의 가독성과 유지보수성을 향상시키며, 파이썬의 표현력을 최대한 활용할 수 있게 해줍니다. 이는 파이썬 데이터 모델(Fluent Python의 제1장 제목이기도 합니다)과 밀접한 연관이 있으며, 파이썬의 기본 요소들이 어떻게 상호작용하는지를 설명합니다.

파이썬 데이터 모델은 특수 메서드(special methods)를 이용해 구현되며, 이 메서드들은 파이썬의 기본 연산을 객체에 적용할 수 있게 합니다. 예를 들어, __len__이나 __getitem__ 같은 특수 메서드를 구현하여, 사용자 정의 객체가 리스트나 딕셔너리처럼 작동하게 만들 수 있습니다.

import collections
 
Card = collections.namedtuple('Card', ['rank', 'suit'])
 
class FrenchDeck:
	ranks = [str(n) for n in range(2, 11)] + list('JQKA')
	suits = 'spades diamonds clubs hearts'.split()
 
	def __init__(self):
		self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
 
	def __len__(self):
		return len(self._cards)
 
	def __getitem__(self, position):
		return self._cards[position]
 
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> from random import choice
>>> choice(deck)
Card(rank='3', suit='hearts')

Fluent Python에 등장하는 위 코드 예시에서 파이썬의 random.choice 함수는 특수 메서드 __getitem__을 이용하여 시퀀스(리스트, 튜플 등)에서 임의의 항목을 선택합니다. 이처럼 특수 메서드를 잘 구현하면, 파이썬의 표준 라이브러리와 잘 통합되는 코드를 작성할 수 있습니다.

또한, '파이써닉'한 코드는 다음과 같은 특징을 가지고 있습니다:

  • 코드가 직관적이고 명확합니다.
  • 파이썬의 기본적인 규칙과 관행을 따릅니다. 예를 들어, len() 함수로 컬렉션의 크기를 얻는 것은 파이썬스럽습니다.
  • with 문이나 리스트 컴프리헨션 같은 파이썬의 고유 기능을 적극적으로 활용합니다.
  • 코드 재사용과 유지보수를 용이하게 하는 디자인 패턴을 사용합니다.

즉, '파이써닉'한 접근 방식은 파이썬의 철학과 일관성, 그리고 효율성을 반영하는 코딩 스타일입니다. 이는 파이썬 커뮤니티에서 널리 인정받고 있으며, 파이썬을 사용하여 깔끔하고 효율적인 코드를 작성하는 데 중요한 역할을 수행합니다.

특수 메서드 사용하기

수치형 타입 흉내 내기

특수 메서드를 사용하면 사용자 정의 객체에서 +와 같은 연산자를 사용할 수 있습니다. 예를 들어, Fluent Python에서는 2차원 벡터를 표현하는 클래스를 구현합니다. 이 클래스는 다음과 같은 특수 메서드를 사용해 연산을 지원합니다:

  • __add__: 두 벡터를 더하는 메서드
  • __mul__: 벡터와 스칼라를 곱하는 메서드
  • __abs__: 벡터의 크기를 계산하는 메서드

이런 특수 메서드를 사용하면 벡터 객체가 내장 수치형 타입처럼 동작하여, 사용자가 수학적 연산을 직관적으로 수행할 수 있습니다.

객체의 문자열 표현

객체의 문자열 표현을 정의하는 특수 메서드에는 __repr____str__가 있습니다.

  • __repr__: 객체의 공식적인 문자열 표현을 반환합니다. 이 표현은 주로 디버깅할 때 사용되며, 가능하다면 객체를 다시 생성하는데 사용할 수 있는 문자열을 반환해야 합니다.
  • __str__: 객체의 비공식적인 문자열 표현을 반환합니다. 이 표현은 주로 사용자에게 친숙한 형식으로 객체를 출력할 때 사용됩니다.

예시로, 벡터 클래스에서 __repr__ 메서드는 Vector(2, 3)와 같은 형식의 문자열을 반환하여 객체의 상태를 명확히 보여줍니다.

객체의 불리언 값

객체가 불리언 문맥에서 참(True)인지 거짓(False)인지 결정하기 위해 __bool____len__ 특수 메서드를 사용할 수 있습니다.

  • __bool__: 객체의 불리언 값을 반환합니다. 이 메서드는 일반적으로 객체가 특정 조건을 만족하는지 판단하는데 사용됩니다.
  • __len__: 객체의 길이를 반환합니다. 길이가 0이면 불리언 문맥에서 거짓으로 간주됩니다.

예를 들어, 벡터 클래스에서 __bool__ 메서드는 벡터의 크기가 0일 때 거짓을, 그 외에는 참을 반환하도록 구현할 수 있습니다.

컬렉션 구현

파이썬의 데이터 모델은 사용자가 컬렉션 타입을 쉽게 구현할 수 있도록 다양한 특수 메서드를 제공합니다. 주요 특수 메서드로는 __len__, __getitem__, __setitem__, __delitem__ 등이 있습니다.

  • __len__: 컬렉션의 길이를 반환합니다.
  • __getitem__: 인덱스를 통해 컬렉션의 항목을 조회하는 기능을 제공합니다.
  • __setitem__: 인덱스를 통해 컬렉션의 항목을 설정하는 기능을 제공합니다.
  • __delitem__: 인덱스를 통해 컬렉션의 항목을 삭제하는 기능을 제공합니다.

우리는 컬렉션처럼 동작하는 FrenchDeck 클래스의 구현을 이미 살펴본 적이 있습니다.

len이 메서드가 아닌 이유

파이썬에서 len 함수는 메서드가 아닌 특수한 함수로 처리됩니다. Fluent Python에서는 이에 대한 여러 가지 이유를 설명하며, 이는 파이썬의 설계 철학과 성능 최적화에 깊은 관련이 있습니다.

성능 최적화

파이썬의 기본 타입, 예를 들어 list, str, memoryview 등은 C 언어로 구현되어 있으며, 이러한 타입의 객체에 대해 len(x)를 호출하면 매우 빠르게 동작합니다. 이는 len 함수가 이러한 객체의 길이를 단순히 C 구조체의 필드에서 읽어오기 때문입니다. 이러한 최적화는 메서드를 호출하는 방식보다 훨씬 빠르며, 많은 객체의 길이를 계산하는 일반적인 작업에서 성능 이점을 제공합니다.

일관성과 실용성

파이썬의 설계 철학(The Zen of Python) 중 하나인 "실용성이 순수성보다 중요하다"는 원칙에 따라 len 함수는 메서드가 아닌 특수 함수로 구현되었습니다. len이 함수로 구현됨으로써 사용자 정의 객체에서도 __len__ 특수 메서드를 구현하여 len 함수를 사용할 수 있게 됩니다. 이는 "특수한 사례가 규칙을 깨뜨릴 정도로 특별하지 않다"는 철학을 따르며, 언어의 일관성과 사용자 정의 객체의 유연성을 높여줍니다.

함수로서의 직관성

len 함수는 마치 수학적인 연산처럼 보이는 단일 인수 함수로 설계되어, 사용자가 직관적으로 이해하기 쉽습니다. 이는 파이썬의 직계 조상인 ABC 언어의 디자인에서 유래되었으며, ABC 언어에서는 # 연산자가 len 함수와 유사하게 동작했습니다. 이러한 함수 형태의 연산은 객체 지향 언어의 메서드 호출 방식보다 더 직관적일 수 있습니다.

사용 편의성

len이 함수로 제공됨으로써 사용자는 객체의 길이를 계산할 때 일관된 방식으로 접근할 수 있습니다. 예를 들어, x.__len__() 대신 len(x)를 사용하면 코드가 더 간결해지고 읽기 쉬워집니다. 이는 파이썬의 다른 내장 함수들(abs, str, repr 등)과 일관된 접근 방식을 제공합니다.