이 강좌에서는 파이썬의 고급 기능을 활용하여 복잡한 문제를 해결하고 효율적인 코드를 작성하는 방법에 대해 알아보겠습니다. 우리가 다루게 될 주요 주제들은 다양한 프로그래밍 패러다임, 고급 데이터 구조, 그리고 파이썬이 제공하는 강력한 내장 모듈 기능들을 포함합니다.
1. 고급 프로그래밍 패러다임
파이썬은 다중 패러다임 프로그래밍 언어입니다. 프로시저형, 객체 지향, 함수형 프로그래밍을 지원하며, 필요에 따라 각각의 장점을 취할 수 있습니다. 이번 장에서는 주로 객체 지향 프로그래밍(OOP)과 함수형 프로그래밍의 고급 기법에 대해 다루겠습니다.
1.1 객체 지향 프로그래밍 심화
OOP의 기본적인 개념은 클래스와 객체의 이해에서 출발합니다. 그러나 보다 복잡한 프로그램을 설계하기 위해서는 다른 개념도 알아야 합니다.
1.1.1 상속과 폴리모르피즘
상속은 새로운 클래스가 기존의 클래스의 속성과 메서드를 물려받는 기능입니다. 상속을 통해 코드의 재사용성을 높일 수 있습니다. 폴리모르피즘은 다른 클래스의 객체들에 대해 동일한 인터페이스를 사용할 수 있게 해줍니다.
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def animal_sound(animal):
print(animal.speak())
dog = Dog()
cat = Cat()
animal_sound(dog) # Woof!
animal_sound(cat) # Meow!
위 예제는 폴리모르피즘의 예시입니다. 각기 다른 클래스의 객체들이 speak()
메서드를 가짐으로써, animal_sound
함수에서 동일한 방식으로 호출이 가능합니다.
1.1.2 추상화와 인터페이스
추상 클래스는 기본 동작을 정의하는 클래스이며, 하나 이상의 추상 메서드를 가집니다. 인터페이스는 이러한 추상 메서드의 집합으로 생각할 수 있습니다. 파이썬에서는 abc
모듈의 ABC
클래스를 통해 추상화를 구현합니다.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.1415 * self.radius * self.radius
circle = Circle(5)
print(circle.area()) # 78.5375
위 예제에서 Shape
클래스는 추상 클래스로써, area
라는 추상 메서드를 정의합니다. Circle
클래스는 Shape
를 상속받아 area
메서드를 구체화합니다.
1.2 함수형 프로그래밍
함수형 프로그래밍은 순수 함수를 사용하여 부작용을 줄이고, 함수 합성을 통해 복잡한 동작을 구현합니다. 파이썬은 이러한 스타일을 장려하기 위해 강력한 함수형 도구를 제공합니다.
1.2.1 람다 함수
람다 함수는 익명의 함수로, 보통 한 줄의 표현식으로 정의됩니다. 짧고 간결한 함수를 작성할 때 유용합니다.
add = lambda x, y: x + y
print(add(5, 3)) # 8
위의 예제에서 lambda
는 두 매개변수를 더하는 익명 함수를 정의합니다.
1.2.2 고차 함수
고차 함수는 함수 자체를 인자로 받거나 반환하는 함수입니다. 파이썬의 map
, filter
, reduce
는 이런 함수형 프로그래밍 기법을 활용한 예입니다.
numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x**2, numbers)
print(list(squared)) # [1, 4, 9, 16, 25]
위의 예제에서 map
함수는 각 리스트 요소에 람다 함수를 적용하여 새로운 이터레이터를 만듭니다.
2. 고급 데이터 구조
고급 데이터 구조 활용은 복잡한 데이터 작업을 보다 효율적으로 수행할 수 있게 합니다. 여기서는 리스트, 딕셔너리 같은 기본 자료형을 넘어, 더 복잡한 데이터 구조를 다룹니다.
2.1 컬렉션 모듈
파이썬의 collections
모듈은 특별한 목적을 가진 여러 데이터 구조를 제공합니다. 그 중 몇 가지를 살펴보겠습니다.
2.1.1 defaultdict
defaultdict
는 존재하지 않는 키를 참조할 때 자동으로 기본값을 생성하는 딕셔너리입니다.
from collections import defaultdict
fruit_counter = defaultdict(int)
fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
for fruit in fruits:
fruit_counter[fruit] += 1
print(fruit_counter) # defaultdict(, {'apple': 3, 'banana': 2, 'orange': 1})
이 예제에서는 defaultdict
를 사용하여 각 과일의 개수를 쉽게 셀 수 있습니다.
2.1.2 namedtuple
namedtuple
은 튜플과 같이 불변하지만, 필드를 이름으로 액세스할 수 있어 코드의 가독성을 높여줍니다.
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
이처럼 namedtuple
을 사용해 필드를 이름으로 접근할 수 있어 명확한 코드를 작성할 수 있습니다.
2.2 힙 큐 모듈
heapq
모듈은 힙 큐 알고리즘을 구현하여, 리스트를 우선순위 큐로 사용할 수 있도록 합니다.
import heapq
numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
heapq.heapify(numbers) # 리스트를 우선순위 큐로 변환
smallest = heapq.heappop(numbers)
print(smallest) # 0
우선순위 큐를 사용하여 데이터의 최솟값을 빠르게 추출할 수 있습니다.
3. 고급 내장 모듈 활용
파이썬의 풍부한 내장 모듈은 다양한 기능을 제공합니다. 여기서는 고급 작업을 위한 몇 가지 모듈을 소개하겠습니다.
3.1 itertools 모듈
itertools
모듈은 반복자를 다루기 위한 유용한 함수를 제공합니다. 반복적인 데이터 처리에 강력한 도구입니다.
3.1.1 combinations와 permutations
조합과 순열은 데이터 집합에서 요소를 선택하는 다양한 방법을 제공합니다.
from itertools import combinations, permutations
data = ['A', 'B', 'C']
# 조합
print(list(combinations(data, 2))) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
# 순열
print(list(permutations(data, 2))) # [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
이러한 기능을 통해 다양한 목록 조합을 빠르게 생성할 수 있습니다.
3.1.2 반복기 집합 다루기
무한 반복, 카운트 증가, 주기 반복 등 다양한 반복 동안 유용하게 사용할 수 있는 도구를 제공합니다.
from itertools import count, cycle
# 무한 카운트
for i in count(10):
if i > 15:
break
print(i, end=' ') # 10 11 12 13 14 15
print() # 새로운 줄
# 주기적 반복
for i, char in zip(range(10), cycle('ABC')):
print(char, end=' ') # A B C A B C A B C A
위 예제는 무한루프와 주기반복의 활용방법을 보여줍니다.
3.2 functools 모듈
functools
모듈은 함수형 프로그래밍 도구를 제공합니다. 특히 함수를 다루는 데 유용한 다양한 도구들을 제공합니다.
3.2.1 lru_cache 데코레이터
@lru_cache
데코레이터는 메모이제이션을 위해 사용되며, 계산된 결과를 저장하여 같은 인풋에 대한 재계산을 피합니다.
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print([fibonacci(n) for n in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
위의 코드에서, 피보나치 수열에 대한 계산 결과가 캐시에 저장되어, 동일한 인풋에 대해 수행시간을 절약할 수 있습니다.
마무리
이 글에서는 파이썬의 고급 주제에 대해 알아보았습니다. 이러한 기능들을 잘 활용하면 복잡한 문제를 효율적으로 해결하고 높은 수준의 코드를 작성할 수 있습니다. 다음 강좌에서 더욱 많은 주제를 다루며 파이썬 전문가로 나아가 보겠습니다.