소개
이번 장에서는 파이썬의 기본 사용법을 넘어 생산성을 극대화하고 더욱 효율적인 코드를 작성하기 위한 고급 주제들을 다룹니다. 이 장을 통해 작성한 코드는 보다 견고하고 확장 가능하며 유지 보수가 쉬워질 것입니다.
1. 컨텍스트 매니저 (Context Managers)
컨텍스트 매니저는 파일 열기, 데이터베이스 연결, 락 사용 등에서 자원 할당과 해제를 자동화하는 파이썬 기능입니다. 이는 코드의 가독성을 높이고, 버그 발생 가능성을 줄여줍니다.
1.1 컨텍스트 매니저의 기본 사용법
파이썬에서 가장 흔한 컨텍스트 매니저의 예는 with
구문을 사용하여 파일을 여는 것입니다.
with open('example.txt', 'r') as file:
data = file.read()
# file은 블록이 종료되면서 자동으로 닫힙니다.
1.2 사용자 정의 컨텍스트 매니저
컨텍스트 매니저를 직접 구현하려면 __enter__
와 __exit__
메소드를 가진 클래스를 정의하면 됩니다.
class CustomContext:
def __enter__(self):
# 자원 할당 혹은 설정
print("자원 할당")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 자원 해제
print("자원 해제")
with CustomContext() as context:
print("블록 내부")
이 예제에서는 블록의 시작과 끝을 알아채고 자원을 관리합니다.
2. 제너레이터 (Generators)
제너레이터는 이터레이터를 간단하게 만든 것이며 큰 데이터 세트를 처리할 때 메모리를 절약할 수 있습니다. 제너레이터는 값을 한 번에 하나씩 반환하여 다음 값을 필요로 할 때까지 대기합니다. 이러한 특성을 통해 제너레이터는 큰 데이터 세트를 효율적으로 처리할 수 있습니다.
2.1 제너레이터 함수
제너레이터 함수는 일반 함수처럼 정의되지만 값을 반환할 때 return
대신 yield
를 사용합니다.
def simple_generator():
yield 1
yield 2
yield 3
위 함수는 호출될 때마다 차례로 1, 2, 3을 반환하는 제너레이터 객체를 만듭니다.
2.2 무한 제너레이터
제너레이터는 무한 루프를 쉽게 만들 수 있어 주기적으로 반복되는 프로세스를 처리할 때 유용합니다.
def infinite_sequence():
num = 0
while True:
yield num
num += 1
이 함수는 중지되지 않는 한 무한하게 0부터 시작하여 증가하는 수를 반환합니다.
3. 데코레이터 (Decorators)
데코레이터는 함수나 메소드의 동작을 동적으로 변경하거나 확장할 수 있는 강력한 도구입니다. 이는 코드 재사용성을 크게 높여주며, 주로 로깅, 접근 제어, 계측 등에 사용됩니다.
3.1 데코레이터의 정의 및 사용
데코레이터는 또 다른 함수로 감싸서 특정 로직을 추가하거나 기존 함수의 입력과 출력을 수정할 수 있습니다.
def my_decorator(func):
def wrapper():
print("함수 호출 전")
func()
print("함수 호출 후")
return wrapper
@my_decorator
def say_hello():
print("안녕하세요!")
say_hello()
이 예제에서 say_hello
함수가 호출될 때 데코레이터가 추가한 기능이 함께 실행됩니다.
3.2 여러 데코레이터 결합
여러 데코레이터를 하나의 함수에 적용할 수 있으며, 이는 데코레이터의 순서에 따라 다르게 동작할 수 있습니다.
def decorator_one(func):
def wrapper():
print("데코레이터 1 적용")
func()
return wrapper
def decorator_two(func):
def wrapper():
print("데코레이터 2 적용")
func()
return wrapper
@decorator_two
@decorator_one
def display():
print("전시 함수")
display()
4. 병렬 처리 및 멀티스레딩 (Parallelism and Multithreading)
프로그램 성능을 높이기 위해 병렬 또는 멀티스레딩을 사용할 수 있습니다. 이는 코드가 여러 CPU 코어를 활용하여 작업을 동시에 수행할 수 있게 해줍니다.
4.1 멀티스레딩
멀티스레딩은 I/O bound 작업이 많은 경우 유용합니다. 파이썬의 threading
모듈을 사용하여 스레드를 만들 수 있습니다.
import threading
import time
def thread_function(name):
print(f"스레드 {name} 시작")
time.sleep(2)
print(f"스레드 {name} 종료")
threads = []
for i in range(3):
thread = threading.Thread(target=thread_function, args=(i,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
이 코드는 3개의 스레드를 만들어 각각 2초간 작업을 수행하게 합니다.
4.2 멀티프로세싱
CPU bound 작업의 경우 multiprocessing
모듈이 더 효율적입니다. 이는 프로세스를 만들어 CPU 코어를 최대한 활용할 수 있게 합니다.
from multiprocessing import Process
def process_function(name):
print(f"프로세스 {name} 시작")
time.sleep(2)
print(f"프로세스 {name} 종료")
processes = []
for i in range(3):
process = Process(target=process_function, args=(i,))
processes.append(process)
process.start()
for process in processes:
process.join()
5. 예외 처리 고급 (Advanced Exception Handling)
예외 처리는 프로그램의 신뢰성을 높이는 데 필수적입니다. 여기서는 고급 예외 처리 기법을 살펴봅니다.
5.1 커스텀 예외 생성
사용자 정의 예외를 만들어 특정 상황에서 발생할 예외를 명시적으로 표현할 수 있습니다.
class CustomError(Exception):
pass
try:
raise CustomError("이것은 사용자 정의 예외입니다")
except CustomError as e:
print(e)
5.2 예외 체이닝
하나의 예외가 다른 예외의 결과일 수 있습니다. 파이썬에서는 raise ... from ...
문법을 사용하여 예외 체인을 만들 수 있습니다.
try:
raise ValueError("첫 번째 예외")
except ValueError as ve:
raise KeyError("두 번째 예외") from ve
6. 결론
이번 장에서는 파이썬의 고급 기능을 보다 깊이 있게 탐구하였습니다. 이러한 기술을 활용하면 더욱 견고하고 확장 가능한 코드를 작성할 수 있습니다. 다음 장에서는 데이터 분석에서 파이썬을 이용하는 방법에 대해 알아보겠습니다.