카테고리 없음

[Python] 파이썬 기초 완전 정리 2단계

강갱갱 2025. 6. 18. 20:30

 

파이썬 완벽 가이드: 3단계로 마스터하기

파이썬은 배우기 쉽고 강력하며 다양한 분야에서 활용되는 인기 있는 프로그래밍 언어입니다. 이 가이드에서는 파이썬의 핵심 개념들을 3단계로 나누어 자세하고 상세하게 설명하여, 프로그래밍 초보자부터 숙련된 개발자까지 모두에게 유익한 정보를 제공하고자 합니다.


2단계: 파이썬 심화 및 객체 지향 프로그래밍 (Advanced & OOP)

1단계에서 파이썬의 기본적인 문법과 흐름 제어를 익혔다면, 이제는 더 복잡한 프로그램을 만들고 코드의 재사용성을 높이는 방법을 배울 차례입니다. 이 단계에서는 파이썬의 심화 문법, 객체 지향 프로그래밍(OOP), 예외 처리, 파일 입출력 등 실질적인 애플리케이션 개발에 필수적인 개념들을 다룹니다.

2.1. 함수 심화

1단계에서 기본적인 함수 사용법을 배웠다면, 이번에는 함수의 다양한 활용법과 고급 기능에 대해 알아봅니다.

가변 인자 (*args, **kwargs)

함수를 호출할 때 인자의 개수가 가변적일 경우 유용하게 사용할 수 있습니다.

  • *args: Positional Arguments를 튜플 형태로 받습니다.
  • **kwargs: Keyword Arguments를 딕셔너리 형태로 받습니다.
def print_args(*args):
    """위치 인자들을 튜플로 받아 출력합니다."""
    print(f"위치 인자들: {args}")

print_args(1, 2, 3, "hello") # (1, 2, 3, 'hello')

def print_kwargs(**kwargs):
    """키워드 인자들을 딕셔너리로 받아 출력합니다."""
    print(f"키워드 인자들: {kwargs}")

print_kwargs(name="Alice", age=30, city="Seoul") # {'name': 'Alice', 'age': 30, 'city': 'Seoul'}

def mix_args(a, b, *args, **kwargs):
    """위치 인자와 키워드 인자를 혼합하여 받습니다."""
    print(f"필수 인자: {a}, {b}")
    print(f"추가 위치 인자: {args}")
    print(f"추가 키워드 인자: {kwargs}")

mix_args(1, 2, 3, 4, name="Bob", job="Developer")
        

람다 (Lambda) 함수

이름 없는(익명) 함수를 한 줄로 간결하게 정의할 때 사용됩니다. 주로 다른 함수의 인자로 전달될 때 유용합니다.

# 일반 함수
def add(x, y):
    return x + y
print(add(1, 2)) # 3

# 람다 함수
add_lambda = lambda x, y: x + y
print(add_lambda(1, 2)) # 3

# filter() 함수와 함께 사용
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"짝수만 필터링: {even_numbers}") # [2, 4, 6, 8, 10]

# map() 함수와 함께 사용
squared_numbers = list(map(lambda x: x * x, numbers))
print(f"제곱 값: {squared_numbers}") # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
        

클로저 (Closure)와 데코레이터 (Decorator) (선택)

함수 심화의 고급 개념으로, 다른 함수를 반환하거나 함수의 동작을 변경할 때 사용됩니다.

  • 클로저: 함수가 그 함수가 정의될 때의 환경(자유 변수)을 기억하여, 함수가 실행될 때 그 환경에 접근할 수 있도록 하는 기능입니다.
  • 데코레이터: 기존 함수의 코드를 수정하지 않고 함수의 기능을 확장하거나 변경할 때 사용되는 파이썬의 특별한 문법입니다. 주로 로깅, 성능 측정, 인증 등에 활용됩니다.
# 클로저 예시
def outer_function(text):
    def inner_function():
        print(text) # outer_function의 text 변수에 접근
    return inner_function

my_closure = outer_function("안녕하세요!")
my_closure() # "안녕하세요!" 출력

# 데코레이터 예시 (간단한 로깅 데코레이터)
def logger(func):
    def wrapper(*args, **kwargs):
        print(f"--- {func.__name__} 함수 시작 ---")
        result = func(*args, **kwargs)
        print(f"--- {func.__name__} 함수 종료 ---")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

@logger
def multiply(a, b):
    return a * b

print(add(10, 20))
print(multiply(5, 6))
        
💡 데코레이터 팁: 데코레이터는 처음에는 복잡하게 느껴질 수 있지만, 웹 프레임워크(Flask, Django) 등에서 라우팅이나 권한 처리 등에 광범위하게 사용되므로 개념을 익혀두면 큰 도움이 됩니다.

2.2. 객체 지향 프로그래밍 (Object-Oriented Programming, OOP)

OOP는 프로그램을 객체들의 상호작용으로 모델링하는 프로그래밍 패러다임입니다. 파이썬은 강력한 객체 지향 기능을 제공하여 코드의 재사용성, 확장성, 유지보수성을 높입니다.

클래스 (Class)와 객체 (Object)

  • 클래스: 객체를 생성하기 위한 "설계도" 또는 "틀"입니다. 속성(데이터)과 메서드(함수)를 정의합니다.
  • 객체 (인스턴스): 클래스를 기반으로 생성된 실제 "실체"입니다. 클래스의 속성과 메서드를 가집니다.
# 클래스 정의
class Dog:
    # 클래스 변수: 모든 인스턴스가 공유하는 속성
    species = "Canis familiaris"

    # 생성자: 객체가 생성될 때 호출되는 특별한 메서드
    def __init__(self, name, breed, age):
        # 인스턴스 변수: 각 객체마다 고유한 속성
        self.name = name
        self.breed = breed
        self.age = age

    # 인스턴스 메서드: 객체가 수행할 수 있는 동작
    def bark(self):
        return f"{self.name}가 멍멍 짖습니다!"

    def get_age_in_human_years(self):
        return self.age * 7

# 객체 (인스턴스) 생성
my_dog = Dog("바둑이", "진돗개", 3)
your_dog = Dog("초코", "푸들", 1)

# 객체의 속성 접근
print(f"내 강아지 이름: {my_dog.name}, 종: {my_dog.species}")
print(f"네 강아지 이름: {your_dog.name}, 종: {your_dog.species}")

# 객체의 메서드 호출
print(my_dog.bark())
print(f"{your_dog.name}는 사람 나이로 {your_dog.get_age_in_human_years()}살 입니다.")
        

상속 (Inheritance)

기존 클래스(부모 클래스)의 속성과 메서드를 물려받아 새로운 클래스(자식 클래스)를 만드는 기능입니다. 코드의 재사용성을 높이고 계층 구조를 형성하는 데 사용됩니다.

class Animal: # 부모 클래스
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError("하위 클래스에서 이 메서드를 구현해야 합니다.")

class Dog(Animal): # Animal 클래스를 상속
    def __init__(self, name, breed):
        super().__init__(name) # 부모 클래스의 생성자 호출
        self.breed = breed

    def speak(self): # 메서드 오버라이딩 (재정의)
        return f"{self.name}가 멍멍!"

class Cat(Animal): # Animal 클래스를 상속
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color

    def speak(self): # 메서드 오버라이딩
        return f"{self.name}가 야옹야옹!"

my_dog = Dog("맥스", "골든 리트리버")
my_cat = Cat("나비", "삼색")

print(my_dog.speak())
print(my_cat.speak())
        

다형성 (Polymorphism)

서로 다른 클래스의 객체들이 동일한 메서드 호출에 대해 각자의 방식으로 응답하는 능력입니다. 상속과 함께 OOP의 핵심 원리 중 하나입니다.

# 위에서 정의한 Animal, Dog, Cat 클래스 활용

animals = [Dog("바비", "시바견"), Cat("미미", "검은색")]

for animal in animals:
    print(animal.speak()) # 각 객체의 speak() 메서드가 호출됨 (다형성)
        

캡슐화 (Encapsulation)

관련된 데이터(속성)와 그 데이터를 조작하는 메서드(함수)를 하나의 단위(클래스)로 묶고, 외부에서의 직접적인 접근을 제한하는 것을 의미합니다. 파이썬에서는 접근 제어자(private, protected)가 없지만, 관례적으로 _ (protected)나 __ (private) 접두사를 사용하여 의도를 나타냅니다.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance # __balance: 외부에서 직접 접근을 제한하는 private 속성으로 간주

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"{amount}원이 입금되었습니다. 잔액: {self.__balance}")
        else:
            print("0원보다 큰 금액을 입금하세요.")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"{amount}원이 출금되었습니다. 잔액: {self.__balance}")
        else:
            print("잔액이 부족하거나 유효하지 않은 금액입니다.")

    def get_balance(self): # 잔액을 조회하는 public 메서드
        return self.__balance

account = BankAccount("김코딩", 10000)
account.deposit(5000)
account.withdraw(2000)
# print(account.__balance) # 직접 접근 시 오류 (AttributeError)
print(f"현재 잔액: {account.get_balance()}원")
account.withdraw(20000)
        

2.3. 예외 처리 (Exception Handling)

프로그램 실행 중 발생할 수 있는 오류(예외)를 미리 예측하고 적절하게 처리하여 프로그램이 비정상적으로 종료되는 것을 방지하는 기법입니다.

  • try: 예외가 발생할 가능성이 있는 코드를 작성합니다.
  • except: try 블록에서 특정 예외가 발생했을 때 실행될 코드를 작성합니다.
  • else: try 블록에서 예외가 발생하지 않았을 때 실행될 코드를 작성합니다.
  • finally: 예외 발생 여부와 상관없이 항상 실행될 코드를 작성합니다 (자원 해제 등에 유용).
def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError: # 0으로 나눌 때 발생하는 예외 처리
        print("오류: 0으로 나눌 수 없습니다.")
        return None
    except TypeError: # 잘못된 타입의 인자를 받을 때 발생하는 예외 처리
        print("오류: 숫자만 입력해주세요.")
        return None
    except Exception as e: # 그 외 모든 예외를 처리
        print(f"알 수 없는 오류 발생: {e}")
        return None
    else:
        print("나눗셈 성공!")
        return result
    finally:
        print("나눗셈 함수 실행 종료.")

print(divide(10, 2))
print(divide(10, 0))
print(divide(10, "a"))
print(divide("b", 2)) # TypeError가 먼저 잡힘
        
⚠️ 중요한 점: 예외 처리는 단순히 오류를 숨기는 것이 아니라, 사용자에게 친화적인 메시지를 제공하고, 프로그램의 안정성을 높이는 데 목적이 있습니다. 너무 광범위한 except Exception 사용은 피하고, 가능한 한 구체적인 예외 타입을 지정하는 것이 좋습니다.

2.4. 파일 입출력 (File I/O)

컴퓨터의 파일 시스템과 상호작용하여 데이터를 읽거나 쓰는 방법을 배웁니다. 데이터를 영구적으로 저장하고 관리하는 데 필수적인 기능입니다.

  • open() 함수: 파일을 열고 파일 객체를 반환합니다.
    • 모드: 'r' (읽기), 'w' (쓰기, 파일이 없으면 생성, 있으면 덮어씀), 'a' (추가 쓰기, 파일 끝에 추가), 'x' (배타적 생성, 파일이 있으면 오류), 'b' (바이너리 모드), 't' (텍스트 모드, 기본값)
  • with open(...) as f:: 파일을 열 때 권장되는 방식입니다. 파일 사용이 끝난 후 자동으로 파일을 닫아주므로 편리하고 안전합니다.
# 텍스트 파일 쓰기 (wt 모드 - write text)
try:
    with open("my_file.txt", "w", encoding="utf-8") as f:
        f.write("안녕하세요, 파이썬 파일 입출력 예제입니다.\n")
        f.write("두 번째 줄입니다.\n")
        print("my_file.txt 파일에 쓰기 완료.")
except IOError as e:
    print(f"파일 쓰기 오류: {e}")

# 텍스트 파일 읽기 (rt 모드 - read text)
try:
    with open("my_file.txt", "r", encoding="utf-8") as f:
        content = f.read() # 파일 전체 내용 읽기
        print("\n--- my_file.txt 내용 (전체 읽기) ---")
        print(content)
except FileNotFoundError:
    print("오류: my_file.txt 파일을 찾을 수 없습니다.")
except IOError as e:
    print(f"파일 읽기 오류: {e}")

# 라인별로 읽기
try:
    with open("my_file.txt", "r", encoding="utf-8") as f:
        print("\n--- my_file.txt 내용 (라인별 읽기) ---")
        for line in f:
            print(line.strip()) # strip()으로 줄바꿈 문자 제거
except FileNotFoundError:
    print("오류: my_file.txt 파일을 찾을 수 없습니다.")

# 텍스트 파일에 내용 추가 (at 모드 - append text)
try:
    with open("my_file.txt", "a", encoding="utf-8") as f:
        f.write("이것은 추가된 내용입니다.\n")
        print("\nmy_file.txt 파일에 내용 추가 완료.")
except IOError as e:
    print(f"파일 추가 쓰기 오류: {e}")

# 추가된 내용 확인
try:
    with open("my_file.txt", "r", encoding="utf-8") as f:
        print("\n--- my_file.txt 내용 (추가 후) ---")
        print(f.read())
except FileNotFoundError:
    print("오류: my_file.txt 파일을 찾을 수 없습니다.")
        
💡 파일 인코딩: 텍스트 파일을 다룰 때는 encoding="utf-8"을 명시하는 것이 좋습니다. 한글 깨짐 현상을 방지할 수 있습니다.

2.5. 고급 자료구조 (Collections 모듈) (선택)

파이썬의 collections 모듈은 내장 자료구조(리스트, 딕셔너리 등)를 확장하거나 특정 목적에 더 적합한 자료구조를 제공합니다.

  • collections.Counter: 해시 가능한 객체들의 개수를 세는 데 사용됩니다. 딕셔너리 서브클래스입니다.
  • collections.deque: 양쪽 끝에서 빠른 추가/삭제가 가능한 덱(Double-ended queue)입니다. 큐나 스택으로 활용하기 좋습니다.
  • collections.defaultdict: 존재하지 않는 키에 접근할 때 기본값을 자동으로 생성해주는 딕셔너리입니다.
from collections import Counter, deque, defaultdict

# Counter 예시
data = ["apple", "banana", "apple", "cherry", "banana", "apple"]
word_counts = Counter(data)
print(f"단어 개수: {word_counts}") # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(f"가장 흔한 단어: {word_counts.most_common(1)}") # [('apple', 3)]

# deque 예시
q = deque()
q.append("A") # 오른쪽 끝에 추가
q.append("B")
q.appendleft("C") # 왼쪽 끝에 추가
print(f"덱: {q}") # deque(['C', 'A', 'B'])
print(f"오른쪽에서 제거: {q.pop()}") # B
print(f"왼쪽에서 제거: {q.popleft()}") # C
print(f"현재 덱: {q}") # deque(['A'])

# defaultdict 예시
# 일반 딕셔너리는 없는 키 접근 시 KeyError 발생
# my_dict = {}
# my_dict['key'] += 1 # 오류

# defaultdict 사용
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
dd = defaultdict(list) # 리스트를 기본값으로 설정
for k, v in s:
    dd[k].append(v)
print(f"그룹화된 딕셔너리: {dd}") # defaultdict(<class 'list'>, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})

word_freq = defaultdict(int) # int (0)를 기본값으로 설정
text = "hello world hello python"
for word in text.split():
    word_freq[word] += 1
print(f"단어 빈도수: {word_freq}") # defaultdict(<class 'int'>, {'hello': 2, 'world': 1, 'python': 1})
        

2.6. 제너레이터 (Generators)와 이터레이터 (Iterators) (선택)

대규모 데이터 처리나 메모리 효율적인 코드 작성에 필수적인 개념입니다.

  • 이터레이터: __iter__()__next__() 메서드를 구현하여 next() 함수로 요소들을 하나씩 순회할 수 있는 객체입니다. for 루프는 내부적으로 이터레이터를 사용합니다.
  • 제너레이터: 이터레이터를 쉽게 만들 수 있는 특별한 종류의 함수입니다. yield 키워드를 사용하여 값을 반환하며, 함수 상태를 기억하고 다음 호출 시점에서 이어서 실행됩니다. 모든 요소를 한꺼번에 메모리에 올리지 않고 필요할 때마다 생성하므로 메모리 효율적입니다.
# 이터레이터 예시
my_list = [1, 2, 3]
my_iterator = iter(my_list) # 리스트로부터 이터레이터 생성

print(next(my_iterator)) # 1
print(next(my_iterator)) # 2
print(next(my_iterator)) # 3
# print(next(my_iterator)) # 더 이상 요소가 없으면 StopIteration 예외 발생

# 제너레이터 함수 예시
def fibonacci_sequence(n):
    a, b = 0, 1
    count = 0
    while count < n:
        yield a # 값을 반환하고, 상태를 저장
        a, b = b, a + b
        count += 1

fib = fibonacci_sequence(5)
print(f"피보나치 수열 (제너레이터): {list(fib)}") # [0, 1, 1, 2, 3]

# 제너레이터 표현식 (Generator Expression)
squares = (x*x for x in range(5))
print(f"제곱 제너레이터: {squares}") #  at ...>
print(f"제곱 값: {list(squares)}") # [0, 1, 4, 9, 16]
        
💡 제너레이터/이터레이터 활용 팁: 대용량 파일을 처리하거나 무한한 시퀀스를 다룰 때 제너레이터는 메모리 효율성을 극대화하여 프로그램 성능을 향상시킬 수 있습니다. 데이터 스트리밍, 로그 파일 파싱 등에 유용합니다.

2.7. 가상 환경 (Virtual Environment)

각 프로젝트마다 독립적인 파이썬 개발 환경을 구축하는 방법입니다. 프로젝트 간의 패키지 충돌을 방지하고 의존성을 관리하는 데 필수적입니다.

  • 주요 도구: venv (파이썬 3.3+ 내장), conda (Anaconda 배포판 사용 시)
  • 생성: python -m venv [환경이름] (예: python -m venv myenv)
  • 활성화:
    • Windows: .\myenv\Scripts\activate
    • macOS/Linux: source myenv/bin/activate
  • 비활성화: deactivate
  • 패키지 설치: 가상 환경 활성화 후 pip install [패키지명]
  • 설치된 패키지 목록 저장: pip freeze > requirements.txt (프로젝트 공유 시 유용)
  • 패키지 일괄 설치: pip install -r requirements.txt
# 가상 환경 생성 (프로젝트 폴더 내에서 실행)
python -m venv myproject_env

# 가상 환경 활성화 (Windows)
.\myproject_env\Scripts\activate

# 가상 환경 활성화 (macOS/Linux)
source myproject_env/bin/activate

# (가상 환경 활성화 후) 필요한 패키지 설치
pip install requests beautifulsoup4

# 설치된 패키지 목록 파일로 저장
pip freeze > requirements.txt

# (다른 환경에서) 필요한 패키지 일괄 설치
pip install -r requirements.txt

# 가상 환경 비활성화
deactivate
        
⚠️ 필수 지식: 파이썬 프로젝트를 시작할 때는 항상 가상 환경을 먼저 생성하고 활성화하는 것을 습관화해야 합니다. 이는 협업 및 배포 시 발생할 수 있는 의존성 문제를 크게 줄여줍니다.
반응형