이번 주 학습 내용
변수와 명명 규칙부터 클래스까지, Python 기초 문법 전반을 다뤘다. 전반적으로 흐름을 따라가기 편한 한 주였다.
01. 변수
Python은 변수 선언 시 타입을 명시하지 않는다. 값을 할당하는 순간 타입이 결정된다.
언더스코어 컨벤션이 인상적이었다. 단순히 스타일 규칙이 아니라 Python이 언더스코어의 위치와 개수로 용도를 구분한다.
_— 무시할 값 또는 마지막 실행 결과.x, _, z = (1, 2, 3)처럼 필요 없는 값을 버릴 때 사용__— name mangling. 클래스 외부에서 직접 접근을 차단하는 private 용도__name__— Python이 예약한 매직 어트리뷰트. 키워드처럼 취급
숫자 리터럴에 _로 자릿수를 구분할 수 있다는 것도 처음 알았다. 1_000_000은 1000000과 동일하다.
02. 자료형
숫자형, 불리언, 문자열, 컬렉션(list / set / dict)을 다뤘다.
- 숫자형 —
0b(2진수),0o(8진수),0x(16진수) 리터럴 표현.//정수 나눗셈,**거듭제곱 연산자 - 문자열 —
strip(),split(),replace(),find()등 메서드 체계. 포매팅은 f-string을 우선 사용. 다른 타입과+연산 불가 — 명시적 형 변환 필요 - list — 순서 있음, 중복 허용, 수정 가능. 인덱싱과 슬라이싱으로 접근
- set — 순서 없음, 중복 제거.
union(|),intersection(&),difference(-),symmetric_difference(^)집합 연산 지원 - dict — key-value 쌍. key는 불변 타입만 허용.
get()으로 키 없을 때 None 반환,keys()/values()/items()로 순회
Python은 암시적 형 변환이 제한적이다. True + 3 = 4, 3 + 5.0 = 8.0은 되지만 3 + "5"는 TypeError다.
03. 제어문
if / elif / else 기본 분기와 삼항 연산자를 복습했다.
- 삼항 연산자 —
값1 if 조건 else 값2형식. 한 줄로 간결하게 표현 가능 - match-case — Python 3.10에서 추가된 패턴 매칭.
switch문과 유사하지만 패턴 기반으로 동작.case _:가 default 역할 - for문 — 리스트, 문자열, 딕셔너리(기본적으로 key 순회) 모두 순회 가능. 중첩 리스트도 언패킹해서 순회할 수 있다
- 리스트 컴프리헨션 —
[val + 'k' for val in a]처럼 한 줄로 리스트 생성. 가독성과 성능 모두 좋다 - break / continue —
break는 반복문 완전 종료,continue는 해당 회차 건너뜀
04. 함수
함수 정의, 인자 전달 방식, 가변 인자, lambda를 다뤘다.
- 기본값 매개변수 —
def greeting(name, greet="처음 뵙겠습니다")처럼 기본값 설정 가능. 기본값 있는 매개변수는 뒤에 위치해야 한다 - 키워드 인자 — 호출 시
information(color='black', name='monkey')처럼 순서 무관하게 전달 가능. 단, 위치 인자 뒤에만 올 수 있다 - 다중 반환 —
return a+b, a-b처럼 여러 값을 반환하면 tuple로 묶인다. 언패킹으로 받을 수 있다 *args— 위치 인자를 tuple로 수집.**kwargs— 키워드 인자를 dict로 수집- lambda — 이름 없는 익명 함수.
map(),filter(),sorted(key=...)에서 자주 활용 - 전달 방식 — 불변 타입(int, str)은 값 복사, 가변 타입(list, dict)은 참조 전달. 함수 안에서 리스트를 수정하면 원본도 바뀐다
05. 클래스
클래스의 기본 구조와 OOP 개념인 캡슐화, 상속을 다뤘다.
- 클래스 변수 vs 인스턴스 변수 — 클래스 변수는 모든 인스턴스가 공유.
__init__에서self.변수로 선언해야 인스턴스마다 독립적으로 생성된다 - 접근 제어 —
__변수명으로 선언하면 name mangling이 적용되어 외부에서 직접 접근 불가. getter/setter 메서드를 통해 제어 - 상속 —
class Student(Person):으로 부모 클래스를 상속.super()로 부모 메서드 호출 - 다중 상속 — Python은 여러 부모 클래스를 동시에 상속할 수 있다. 같은 이름의 메서드가 있을 경우 MRO(Method Resolution Order) 순서로 결정
KPT 회고
Keep
모르는 문법이 나왔을 때 그냥 넘기지 않고 직접 실행해보며 확인하는 방식이 좋았다. 특히 set 집합 연산이나 언더스코어 컨벤션처럼 평소에 대충 알고 있던 부분을 코드로 확인하니 확실히 정리됐다.
이 흐름을 다음 주에도 유지하자.
Problem
쉽다는 생각에 손으로 따라치기보다 눈으로만 훑고 넘어간 부분이 있었다. 나중에 직접 써보려니 막히는 메서드 이름이나 인자 순서가 있었다.
클래스 실습에서 오타로 인한 에러를 찾는 데 생각보다 시간이 걸렸다.
Try
예제를 그대로 따라치는 것에서 벗어나 변형 예제를 직접 만들어보는 방식으로 전환한다. 예를 들어 dict 메서드를 배우면 그 자리에서 간단한 학생 성적부 dict를 만들어보는 식으로.
새롭게 발견한 것
클래스 변수의 공유 함정. 클래스 변수로 선언한 리스트는 모든 인스턴스가 공유한다. 인스턴스에서 append로 수정하면 클래스 변수 자체가 바뀌어버린다.
# 잘못된 예 — skills가 모든 인스턴스에 공유됨
class Character:
skills = [] # 클래스 변수
def add_skill(self, skill):
self.skills.append(skill) # 클래스 변수를 직접 수정
my = Character()
your = Character()
my.add_skill('곤봉 휘두르기')
your.add_skill('마법진')
print(my.skills) # ['곤봉 휘두르기', '마법진'] — 의도와 다름
# 올바른 예 — __init__에서 인스턴스 변수로 선언
class Character:
def __init__(self):
self.skills = [] # 인스턴스마다 독립적으로 생성언더스코어 컨벤션. 별도의 키워드 없이 이름 규칙만으로 접근 제어와 용도를 표현한다는 점이 Python다웠다. _는 무시할 값, __는 외부 접근 차단, __name__은 Python 예약 어트리뷰트.
트러블슈팅
문제 : super().__init__() 호출 시 TypeError 발생
다단계 상속에서 super().__init__()을 호출할 때 self를 명시적으로 넘겼더니 인자 개수 오류가 발생했다.
# 수정 전 — self를 직접 전달
class Student(Learner):
def __init__(self, name, subject):
super().__init__(self, subject) # TypeError!
self.name = name
# TypeError: Learner.__init__() takes 2 positional arguments but 3 were givensuper().__init__()은 이미 인스턴스에 바인딩된 메서드를 반환하기 때문에 self가 자동으로 전달된다. 여기에 self를 한 번 더 넘기면 인자가 하나 초과된다.
# 수정 후 — self 제거
class Student(Learner):
def __init__(self, name, subject):
super().__init__(subject) # 정상 동작
self.name = name습관적으로 self를 넣었다가 생긴 실수였다. super().메서드()는 이미 self가 바인딩된 상태임을 기억하자.
다음 주 목표
- 모듈, 패키지, 파일 I/O, 예외 처리 학습
- 예제를 그대로 따라치지 않고 변형 예제를 직접 만들어보기
- Python 공식 문서를 병행해서 읽는 습관 들이기
기초를 다시 밟는 이번 주가 나중에 가장 단단한 발판이 될 것이다.