정처기 실기 프로그래밍: 파이썬
정처기 실기

정처기 실기 프로그래밍: 파이썬

작성일: 2026년 07월 15일0

Python은 값을 대입하는 순간 그 값을 보고 자료형을 자동으로 결정합니다(동적 타이핑). 같은 변수라도 새 값을 대입하면 자료형이 바뀔 수 있습니다.

기본 자료형 6종

분류자료형설명예시
숫자형int정수 (소수점 없음, 크기 제한 없음)100, -5, 0
숫자형float실수 (소수점이 있는 수)3.14, 100.0
논리형bool참 / 거짓True, False
문자열str문자들의 묶음"hello", "100.0"
시퀀스list변경 가능한 묶음 (mutable)[1, 2, 3]
시퀀스tuple변경 불가능한 묶음 (immutable)(1, 2, 3)

변수와 동적 타이핑

x = 5         # int (정수)
y = 3.14      # float (실수)
z = "감자"    # str (문자열)
t = (1, 2)    # tuple (튜플)

x = 5         # x는 int
x = "감자"    # x는 str로 바뀜 (재대입하면 자료형도 바뀜)

int와 float의 차이는 점(.) 하나

같아 보이는 두 숫자라도 점이 있으면 float, 없으면 int입니다. 시험 단골 함정입니다.

a = 100        # int
b = 100.0      # float (점이 있어서 실수)

print(type(a))            # <class 'int'>
print(type(b))            # <class 'float'>
print(type(100) == type(100.0))   # False (값은 같아 보여도 자료형이 다름)

bool은 int의 하위 자료형

True는 1, False는 0으로 동작해 산술 연산에 쓸 수 있습니다.

print(True + True)   # 2  (1 + 1)
print(True + 1)      # 2
print(False * 100)   # 0

type() — 자료형 확인

type(x)는 항상 <class '자료형'> 형태로 결과를 보여줍니다.

print(type(100))      # <class 'int'>
print(type(100.0))    # <class 'float'>
print(type("100"))    # <class 'str'>
print(type((1, 2)))   # <class 'tuple'>
print(type([1, 2]))   # <class 'list'>

type(x) == type(샘플) 형태로 두 값이 같은 자료형인지 비교하는 관용구가 기출에 나옵니다.

x = 100
print(type(x) == type(100))    # True  (둘 다 int)
print(type(x) == type(""))     # False (int vs str)

len() — 요소 개수

문자열·리스트·튜플·딕셔너리 등의 요소 개수를 반환합니다. 문자열에서는 공백·점·특수기호도 한 글자로 셉니다.

print(len("hello"))           # 5
print(len("a b"))             # 3  (공백도 한 글자)
print(len("100.0"))           # 5  (문자 '1','0','0','.','0')
print(len(""))                # 0  (빈 문자열)
print(len([1, 2, 3]))         # 3
print(len((100, 200)))        # 2
print(len({"a": 1, "b": 2}))  # 2  (키-값 쌍의 개수)

산술 연산자

연산자이름예시결과
+덧셈5 + 38
-뺄셈5 - 32
*곱셈5 * 315
/실수 나눗셈 (항상 float)10 / 33.333...
//정수 나눗셈 (소수점 버림)10 // 33
%나머지 (모듈로)5 % 32
**거듭제곱2 ** 101024

Python의 /는 항상 실수를 반환합니다. C/Java와 달리 10 / 33.333...입니다. 정수 몫이 필요하면 //를 씁니다.

print(10 / 3)    # 3.3333333333333335
print(10 // 3)   # 3
print(2 ** 10)   # 1024

나머지 연산 활용 (자릿수·짝홀·배수)

# 짝수/홀수 판별
if n % 2 == 0:
    print("짝수")
else:
    print("홀수")

# 자릿수 추출
n = 123
ones = n % 10          # 3  (1의 자리)
tens = (n // 10) % 10  # 2  (10의 자리)

# 순환 (0 → 1 → 2 → 0 → ...)
index = (index + 1) % 3

비교 연산자 — 결과는 True / False

연산자의미예시결과
==같다 (값 비교)5 == 5True
!=같지 않다5 != 3True
< > <= >=대소 비교3 < 5True
is / is not같은 객체인가 (주소 비교)None is NoneTrue
in / not in포함되어 있는가3 in [1, 2, 3]True

is / in

==값이 같은지, is같은 객체(주소)인지를 확인합니다. None과 비교할 땐 is None이 관례입니다.

x = None
if x is None:       # 권장
    print("값 없음")

nums = [1, 2, 3]
print(3 in nums)          # True
print(10 not in nums)     # True
print("py" in "python")   # True

grades = {"철수": 90}
print("철수" in grades)   # True  (in은 key만 확인)
print(90 in grades)       # False (value는 확인 대상 아님)

논리 연산자

연산자설명예시결과
and두 조건 모두 True이면 TrueTrue and FalseFalse
or하나라도 True이면 TrueTrue or FalseTrue
not참/거짓 반전not TrueFalse

input() — 입력은 항상 문자열

input()은 키보드 입력을 문자열로 반환합니다. 숫자로 쓰려면 int()로 변환합니다.

name = input("이름을 입력하세요: ")   # 문자열
age = int(input())                    # 정수로 변환

print() 기본 동작 — 자동 줄바꿈

print("A")
print("B")
# 출력:
# A
# B

여러 인자와 sep (사이에 공백 한 칸 자동 삽입)

쉼표로 값을 나열하면 각 값 사이에 공백 한 칸이 자동으로 들어갑니다.

print("x", 1, "y", 2)          # x 1 y 2
print('a=', 20)                # a= 20  (등호와 20 사이에 공백!)

print("a", "b", "c")           # a b c  (기본 공백)
print("a", "b", "c", sep="")   # abc    (구분 없음)
print("a", "b", "c", sep="/")  # a/b/c

end (끝 문자 변경) + 빈 print()

print("A", end="")
print("B", end="")
# 출력: AB (같은 줄)

# 중첩 for에서 안쪽 끝나면 줄바꿈
lol = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
for sub in lol:
    for item in sub:
        print(item, end="")
    print()
# 출력:
# 123
# 45
# 6789

f-string — 중괄호 안 값이 치환

name = "감자"
age = 20
print(f"이름: {name}, 나이: {age}")   # 이름: 감자, 나이: 20
print(f"합계: {3 + 4}")               # 합계: 7  (계산식도 가능)

f-string 포맷 지정자 — {값:포맷}

포맷의미예시 → 결과
:5d정수, 최소 폭 5 (오른쪽 정렬)f"{7:5d}"" 7"
:04d폭 4, 빈자리 0으로 채움f"{7:04d}""0007"
:.2f소수점 이하 2자리 (반올림)f"{3.14159:.2f}""3.14"
:<5왼쪽 정렬, 폭 5f"{'A':<5}""A "
:>5오른쪽 정렬, 폭 5f"{'A':>5}"" A"
:^5가운데 정렬, 폭 5f"{'A':^5}"" A "
pi = 3.14159
print(f"{pi:.2f}")   # 3.14
print(f"{pi:.0f}")   # 3      (반올림)
print(f"{pi:8.2f}")  # "    3.14"  (폭 8, 소수점 2자리)

% 포맷팅 (옛 방식, 기출에 함께 출제)

자리표시자받는 값
%s문자열 (string)
%d정수 (digit)
%f실수 (float)
print("%s" % "hi")               # hi
print("%d점" % 100)              # 100점
print("%s %d점" % ("감자", 100)) # 감자 100점  (값 여러 개는 튜플로)

인접 문자열 자동 결합

문자열 리터럴이 공백으로 나란히 있으면 + 없이 자동으로 이어 붙습니다(f-string도 가능). 변수에는 적용되지 않습니다.

s = "abc" "def"
print(s)   # abcdef

print(f'ab{2+1}' f'ca{3}')   # ab3ca3  (각 f-string 계산 후 결합)

문자열 만들기

홑/쌍/삼중 따옴표 모두 동일한 str입니다. Python에는 문자(char) 타입이 따로 없습니다.

name = "감자"
greeting = '안녕하세요'
long_text = """여러 줄에 걸친
문자열입니다"""

s1 = "it's fine"      # 쌍따옴표 밖, 홑따옴표 안 (이스케이프 불필요)
s2 = 'say "hello"'    # 홑따옴표 밖, 쌍따옴표 안

함정: 따옴표로 감싸면 숫자처럼 보여도 str입니다.

a = "100"      # str
c = "100.0"    # str
print(type(a))  # <class 'str'>

인덱싱 (0부터, 음수는 뒤에서)

s = "PYTHON"
print(s[0])    # P  (첫 번째)
print(s[5])    # N  (여섯 번째)
print(s[-1])   # N  (마지막)
print(s[-2])   # O  (뒤에서 두 번째)

슬라이싱 s[시작:끝:step] — 끝 인덱스 미포함

s = "PYTHON"
print(s[0:3])   # PYT   (인덱스 0, 1, 2)
print(s[:3])    # PYT   (처음부터 인덱스 2까지)
print(s[3:])    # HON   (인덱스 3부터 끝까지)
print(s[1:4])   # YTH   (인덱스 1, 2, 3 — 4는 미포함)
print(s[::2])   # PTO   (2칸 간격)
print(s[::-1])  # NOHTYP (역순 = 뒤집기)

split() — 문자열을 리스트로 쪼갬

s = "hello world"
parts = s.split(' ')
print(parts)     # ['hello', 'world']
print(parts[0])  # hello

구분자는 결과에 포함되지 않습니다(시험 함정).

join() — 이터러블을 문자열로 합침 (split의 반대)

print(' '.join(['hello', 'world']))   # "hello world"
print(','.join(['A', 'B', 'C']))      # "A,B,C"
print(''.join(['a', 'b', 'c']))       # "abc"

# 숫자는 str()로 변환 후 넣어야 함 (안 하면 TypeError)
print(','.join(str(n) for n in [1, 2, 3]))   # "1,2,3"

# 구분자 교체: split + join
print(','.join('hello world'.split(' ')))    # "hello,world"

기타 문자열 메서드

메서드동작예시 → 결과
strip()양 끝 공백/문자 제거 (안쪽은 그대로)" hi ".strip()"hi"
replace(old, new)모든 old를 new로 치환"hello".replace("l","L")"heLLo"
find(x)x가 처음 나오는 인덱스"hello".find("l")2
upper() / lower()대문자 / 소문자로"py".upper()"PY"
startswith(x)x로 시작하는지"hello".startswith("he")True

split()리스트를 돌려주고 나머지는 문자열/정수 1개를 돌려줍니다. 그래서 a, b = ... 다중 할당 빈칸에는 split()만 들어갈 수 있습니다.

리스트와 튜플의 가장 큰 차이는 변경 가능 여부입니다.

항목리스트튜플
괄호대괄호 [ ]소괄호 ( )
변경 가능 여부변경 가능 (mutable)변경 불가 (immutable)
요소 추가/삭제append, pop 등 가능불가
메서드다양count, index
nums = [1, 2, 3]
nums[0] = 100     # 리스트: 가능

point = (1, 2, 3)
# point[0] = 100  # TypeError! 튜플은 변경 불가

튜플 만들기 — 쉼표가 핵심

t = 10, 20, 30       # 괄호 없이 콤마만으로도 튜플
a = (100)            # int! (그냥 괄호 친 정수)
b = (100,)           # tuple (끝에 쉼표 필수)
empty = ()           # 빈 튜플

print(type(a))   # <class 'int'>
print(type(b))   # <class 'tuple'>

함정: (100)은 튜플이 아닙니다. 요소 1개 튜플은 (100,)처럼 쉼표를 붙여야 합니다.

tuple() / list() 변환

print(tuple([1, 2, 3]))   # (1, 2, 3)
print(tuple("abc"))       # ('a', 'b', 'c')  (한 글자씩)
print(tuple(range(5)))    # (0, 1, 2, 3, 4)
print(list((10, 20)))     # [10, 20]

슬라이싱 (리스트·튜플 공통, 끝 인덱스 미포함)

t = (0, 1, 2, 3, 4, 5)
print(t[1:4])   # (1, 2, 3)
print(t[:3])    # (0, 1, 2)
print(t[3:])    # (3, 4, 5)
print(t[::2])   # (0, 2, 4)
print(t[::-1])  # (5, 4, 3, 2, 1, 0)  (역순)

lst = list(range(10))
print(lst[2:6])   # [2, 3, 4, 5]
print(lst[::2])   # [0, 2, 4, 6, 8]
print(lst[::-2])  # [9, 7, 5, 3, 1]  (음수 간격: 끝에서 앞으로)

연산과 포함 여부

print((1, 2) + (3, 4))   # (1, 2, 3, 4)  (연결, 새 튜플)
print((0,) * 5)          # (0, 0, 0, 0, 0)  (반복)
print((1, 2) * 3)        # (1, 2, 1, 2, 1, 2)
print("바나나" in ("사과", "바나나"))   # True

튜플 메서드 (count, index)

t = (1, 2, 3, 2, 4, 2)
print(t.count(2))   # 3  (2가 3개)
print(t.index(2))   # 1  (2가 처음 나오는 인덱스, 없으면 ValueError)

딕셔너리 — 키-값 쌍

d = {"name": "홍길동", "age": 25}
print(d["name"])    # 홍길동
d["age"] = 30       # 수정
d["score"] = 95     # 새 키 추가

print(d.keys())     # dict_keys(['name', 'age', 'score'])
print(d.values())   # dict_values(['홍길동', 30, 95])
print(d.items())    # dict_items([('name','홍길동'), ...])

순회 + 집계

score = {"국어": 90, "영어": 85, "수학": 95}
for k, v in score.items():   # items()는 (키, 값) 튜플
    print(k, v)

print(sum(score.values()))   # 270  (값의 합)
print(len(score))            # 3    (키-값 쌍의 개수)

get(key, default) — 키가 없을 때 기본값

d["x"]는 키가 없으면 KeyError, get()은 기본값을 돌려줍니다.

d = {"a": 1, "b": 2}
print(d.get("a"))      # 1
print(d.get("x"))      # None  (기본값 생략 시)
print(d.get("x", 0))   # 0

빈도수 카운팅 (최빈출 패턴)

s = "banana"
freq = {}
for ch in s:
    freq[ch] = freq.get(ch, 0) + 1
print(freq)                # {'b': 1, 'a': 3, 'n': 2}
print(sum(freq.values()))  # 6  (전체 글자 수)
print(len(freq))           # 3  (서로 다른 글자 수)

get(ch, 0)이 첫 등장도 0에서 출발시켜 if-else 분기 없이 한 줄로 처리됩니다. d.get(ch)처럼 기본값을 생략하면 None + 1에서 TypeError가 납니다.

집합 (set) — 중복 없음, 순서 없음

s = {1, 2, 2, 3, 3, 3}
print(s)          # {1, 2, 3}  (중복 자동 제거)
empty = set()     # 빈 집합 ({}는 빈 딕셔너리!)

함정: 빈 {}는 집합이 아니라 빈 딕셔너리입니다. 빈 집합은 set().

집합 메서드와 연산

연산기호설명
교집합&두 집합에 모두 있는 요소
합집합|두 집합의 모든 요소
차집합-앞 집합에만 있는 요소
대칭 차집합^한쪽에만 있는 요소
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
print(a & b)   # {3, 4, 5}
print(a | b)   # {1, 2, 3, 4, 5, 6, 7}
print(a - b)   # {1, 2}
print(a ^ b)   # {1, 2, 6, 7}

s = {1, 2, 3}
s.add(4)              # {1, 2, 3, 4}
s.remove(2)           # {1, 3, 4}  (없으면 KeyError)
s.discard(10)         # 없어도 오류 없음
s.update([5, 6, 7])   # 여러 개 한 번에 추가

if / elif / else

Python은 중괄호 대신 콜론(:)과 들여쓰기로 블록을 구분합니다.

if score >= 90:
    print("A")
elif score >= 80:
    print("B")
else:
    print("C")

for + range

for i in range(5):
    print(i, end=" ")
# 출력: 0 1 2 3 4

fruits = ["사과", "바나나", "체리"]
for i in range(len(fruits)):
    print(i, fruits[i])

while

while은 조건이 참인 동안 반복합니다. 변수 변경(i += 1)을 빠뜨리면 무한 루프가 됩니다.

i = 0
while i < 3:
    print(i, end=" ")
    i += 1
# 출력: 0 1 2

break / continue

for i in range(10):
    if i == 3:
        break        # 즉시 종료 (3은 출력 안 됨)
    print(i, end=" ")
# 출력: 0 1 2

for i in range(5):
    if i == 2:
        continue     # 건너뛰고 다음 반복
    print(i, end=" ")
# 출력: 0 1 3 4

def — 함수 정의

Python은 main 함수가 필수가 아니며, 함수는 호출 전에 정의되어야 합니다(아래에 있으면 NameError).

def gamja(a, b):
    return a + b

result = gamja(3, 5)
print(result)   # 8

return이 없으면 None을 반환합니다.

def greet():
    print("hi")
print(greet())   # hi 다음 줄에 None

lambda — 이름 없는 익명 함수 (한 줄)

lambda 매개변수: 반환식 형태로, 함수 이름과 return을 생략합니다.

일반 함수 (def)lambda
def add(x): return x + 100lambda x: x + 100
여러 줄 가능단일 표현식만 가능
add_100 = lambda num: num + 100
print(add_100(5))    # 105

multiply = lambda x, y: x * y   # 매개변수 여러 개
print(multiply(3, 4))           # 12

clamp = lambda x: x if x > 0 else 0   # 삼항 연산 가능
print(clamp(-3))                      # 0

if/for 같은 복잡한 로직은 lambda로 쓸 수 없습니다. 그럴 땐 def를 씁니다.

range() — 정수 범위 (끝 미포함)

list(range(5))          # [0, 1, 2, 3, 4]
list(range(2, 6))       # [2, 3, 4, 5]
list(range(0, 10, 3))   # [0, 3, 6, 9]
list(range(5, 0, -1))   # [5, 4, 3, 2, 1]  (역순)
print(range(10))        # range(0, 10)  (그대로 출력하면 범위 정보)

range(5)는 0~4를 생성하고 5는 포함하지 않습니다(C/Java의 i < 5와 동일).

sum() — 숫자 요소의 합

print(sum([1, 2, 3, 4, 5]))   # 15
scores = [80, 90, 70, 100]
print(sum(scores) / len(scores))   # 85.0  (평균 패턴)

sorted() — 정렬된 새 리스트 (원본 유지)

nums = [3, 1, 4, 1, 5]
print(sorted(nums))                 # [1, 1, 3, 4, 5]
print(nums)                         # [3, 1, 4, 1, 5]  (원본 그대로)
print(sorted(nums, reverse=True))   # [5, 4, 3, 1, 1]

sorted(x)는 새 리스트 반환, x.sort()는 원본을 정렬하고 None 반환.

key=lambda — 정렬 기준 바꾸기

data = [("A", 90), ("B", 70), ("C", 85)]
print(sorted(data, key=lambda x: x[1]))
# [('B', 70), ('C', 85), ('A', 90)]  (두 번째 원소 기준)

# 딕셔너리를 값 기준 정렬 (최빈값 뽑기)
freq = {'b': 1, 'a': 3, 'n': 2}
top = sorted(freq.items(), key=lambda x: x[1], reverse=True)[0]
print(top)   # ('a', 3)

key에는 값이 아니라 함수를 넘겨야 합니다(key=x[1]은 오류).

enumerate() — 인덱스와 값 동시에

for i, v in enumerate(["사과", "바나나"]):
    print(i, v)
# 0 사과
# 1 바나나

for i, v in enumerate(["a", "b"], start=1):   # 시작 번호 변경
    print(i, v)
# 1 a 그리고 2 b

map() — 각 요소에 함수 적용 (map 객체 반환)

nums = ["1", "2", "3"]
print(list(map(int, nums)))   # [1, 2, 3]
print(list(map(lambda x: x + 100, [1, 2, 3])))  # [101, 102, 103]
print(map(str, [1, 2]))       # <map object at 0x...>  (list()로 감싸야 함)

all() / any()

all은 모두 참이면 True, any는 하나라도 참이면 True. 숫자 0은 False, 0이 아니면 True.

print(all([1, 2, 3]))    # True
print(all([1, 0, 3]))    # False  (0은 False)
print(any([0, 0, 1]))    # True
print(all([]))           # True   (거짓인 요소가 없음)
print(any([]))           # False  (참인 요소가 없음)

같은 공식을 괄호 모양만 바꿔 4종의 자료구조를 만듭니다.

종류형식반환 타입
리스트[식 for 변수 in 반복]list
딕셔너리{키: 값 for ...}dict
집합{식 for ...} (콜론 없음)set
제너레이터(식 for ...)generator

괄호 모양으로 자료형이 갈립니다

nums = [1, 2, 3]
a = [x * 2 for x in nums]       # 대괄호 → list
b = {x: x * 2 for x in nums}    # 중괄호 + : → dict
c = {x * 2 for x in nums}       # 중괄호 (콜론 없음) → set
d = (x * 2 for x in nums)       # 소괄호 → generator

print(type(a))   # <class 'list'>
print(type(b))   # <class 'dict'>
print(type(c))   # <class 'set'>
print(type(d))   # <class 'generator'>

딕셔너리와 집합은 같은 중괄호를 쓰므로 콜론(:) 유무가 결정적 차이입니다. 빈 {}는 딕셔너리입니다.

조건 필터링 + 예시

x = [1, 2, 3, 4, 5, 6]
print([n for n in x if n % 2 == 0])         # [2, 4, 6]
print([n ** 2 for n in [1, 2, 3, 4]])       # [1, 4, 9, 16]
print({i: i * 2 for i in [1, 2, 3]})        # {1: 2, 2: 4, 3: 6}
print({n for n in [1, 2, 2, 3, 3]})         # {1, 2, 3}  (중복 제거)

제너레이터와 게으른 평가 + next()

제너레이터는 값을 미리 만들지 않고 next()/for로 요청할 때마다 하나씩 계산합니다(메모리 절약).

gen = (x * 2 for x in [1, 2, 3])
print(next(gen))   # 2
print(next(gen))   # 4
print(next(gen))   # 6
# 한 번 더 next(gen) → StopIteration 예외

# sum/any/all/join 인자로 넣을 땐 괄호 생략 가능
print(sum(x for x in [1, 2, 3, 4, 5] if x % 2 == 0))  # 6 (2+4)
print(''.join(c for c in "Hello" if c.islower()))     # "ello"

제너레이터는 한 번 소비하면 빈 상태가 됩니다. 재사용하려면 리스트 컴프리헨션을 쓰세요.

기본 구조와 init 생성자

__init__은 인스턴스 생성 시 자동 호출되는 생성자입니다. 첫 매개변수는 반드시 self(인스턴스 자신, Python이 자동 전달).

class Node:
    def __init__(self, v):
        self.v = v      # 인스턴스 속성
        self.c = []     # 자식 목록

n = Node(10)   # __init__(self, 10) 자동 호출
print(n.v)     # 10
print(n.c)     # []

Node(10) 호출 시 self는 자동으로 넘어가고 사용자는 v만 전달합니다.

클래스 속성 vs 인스턴스 속성

구분정의 위치공유 여부
클래스 속성클래스 블록 (메서드 밖)모든 인스턴스가 공유
인스턴스 속성__init__ 안 (self.속성)인스턴스마다 독립
class Gamja:
    li = [1, 2, 3]        # 클래스 속성 (공유)

print(Gamja().li)   # [1, 2, 3]
print(Gamja.li)     # [1, 2, 3]  (클래스명으로도 접근)

함정: 클래스 속성으로 리스트를 두면 모든 인스턴스가 같은 리스트를 공유합니다. 독립이 필요하면 __init__ 안에서 self.c = []로 정의합니다.

인스턴스 메서드 (첫 인자 self)

class Bag:
    def __init__(self):
        self.items = []
    def add(self, x):
        self.items.append(x)   # self로 속성 조작
    def total(self):
        return sum(self.items)

b = Bag()
b.add(10); b.add(20); b.add(30)   # add(b, 10) 로 전달됨
print(b.items)    # [10, 20, 30]
print(b.total())  # 60

self를 빼먹으면 takes 1 positional argument but 2 were given 오류가 납니다.

상속과 super()

class Shape:
    def __init__(self, name):
        self.name = name
    def area(self):
        return 0

class Circle(Shape):              # Shape 상속
    def __init__(self, r):
        super().__init__("원")    # 부모 __init__ 호출
        self.r = r
    def area(self):               # 오버라이딩
        return 3.14 * self.r * self.r

c = Circle(5)
print(c.name)    # 원    (부모가 넣어 준 속성)
print(c.area())  # 78.5  (자식이 덮어쓴 메서드)

자식이 __init__을 새로 정의하면 부모 __init__은 자동 호출되지 않습니다. super().__init__(...)을 빠뜨리면 부모가 세팅할 속성이 비어 AttributeError가 납니다.

재귀 함수 = 자기 자신을 호출하는 함수. 항상 기저 조건(멈추는 지점) + 재귀 호출로 이뤄집니다. 기저 조건이 없으면 RecursionError.

def f(n):
    if n == 1:            # 기저 조건
        return 1
    return n * f(n - 1)   # 재귀 호출

팩토리얼 추적 (한 번 호출)

def f(n):
    if n == 1:
        return 1
    return n * f(n - 1)
print(f(5))   # 120
# f(5)=5*f(4), f(4)=4*f(3), ... f(1)=1
# 거꾸로: 1 → 2 → 6 → 24 → 120

피보나치 (두 번 호출 = 다중 재귀)

def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)
print(fib(5))   # 5
# fib: 0,1,1,2,3,5 ...

중첩 리스트 평탄화

def flatten(x):
    if not isinstance(x, list):   # 기저: 리스트 아니면 [x]
        return [x]
    result = []
    for item in x:
        result += flatten(item)   # 재귀 결과 이어 붙이기
    return result

print(flatten([1, [2, [3, 4]], 5]))       # [1, 2, 3, 4, 5]
print(sum(flatten([1, [2, [3, 4]], 5])))  # 15

다중 재귀 추적 전략 (핵심)

호출을 기저 조건까지 펼친 뒤(왼쪽 먼저), 가장 작은 값부터 거꾸로 계산합니다. 기저 조건이 1이 아니라 n을 반환하는 경우 주의(예: n <= 1: return n이면 n=-1일 때 -1 반환).

def gamja(n):
    if n <= 1:
        return n
    return gamja(n - 1) + gamja(n - 3)
print(gamja(7))   # 2

2차원 리스트에서 자주 틀리는 함정입니다.

복사 방법바깥 리스트안쪽 요소
b = m같은 객체 (복사 아님)같은 객체
b = m[:]새 객체같은 객체 (공유)
b = list(m)새 객체같은 객체 (공유)
b = copy.deepcopy(m)새 객체새 객체

얕은 복사 — 바깥만 복사, 안쪽은 공유

m = [[1], [2], [3]]
b = m[:]                 # 얕은 복사
print(b == m)            # True   (값이 같음)
print(b is m)            # False  (다른 바깥 리스트)
print(b[0] is m[0])      # True   (안쪽은 같은 객체)

append vs += 함정

m = [[1], [2]]
b = m[:]
b.append([99])   # 바깥에 추가 → m 영향 없음
print(m)         # [[1], [2]]

m = [[1], [2]]
b = m[:]
b[0] += [99]     # 안쪽 리스트 제자리 수정 → m도 바뀜
print(b)         # [[1, 99], [2]]
print(m)         # [[1, 99], [2]]  (함께 바뀜!)

깊은 복사 — copy.deepcopy()

import copy
m = [[1], [2], [3]]
b = copy.deepcopy(m)
b[0] += [99]
print(b)   # [[1, 99], [2], [3]]
print(m)   # [[1], [2], [3]]  (영향 없음)

다중 할당 / 튜플 언패킹

= 왼쪽 변수 개수와 오른쪽 값 개수가 같아야 합니다(다르면 ValueError).

a, b = 1, 2
x, y, z = 10, 20, 30

point = (10, 20)
x, y = point      # x=10, y=20

data = [100, 200, 300]
a, b, c = data    # 리스트도 언패킹

swap — 임시 변수 없이 한 줄 교환

버블 정렬 등에서 자주 출제됩니다.

a, b = 1, 2
a, b = b, a       # 오른쪽 (2, 1) 튜플이 왼쪽에 풀림
print(a, b)       # 2 1

for 루프 / enumerate / dict.items() 언패킹

for num, ch in [(1, "a"), (2, "b")]:
    print(num, ch)

for i, fruit in enumerate(["사과", "바나나"]):
    print(i, fruit)

for key, value in {"a": 1, "b": 2}.items():
    print(key, value)

함수 반환값 언패킹 (divmod 등)

def min_max(nums):
    return min(nums), max(nums)   # 콤마 반환 → 튜플
small, large = min_max([3, 1, 4, 1, 5])
print(small, large)   # 1 5

q, r = divmod(10, 3)  # (몫, 나머지)
print(q, r)           # 3 1

a, b = input().split()   # "10 20" 입력 → a="10", b="20" (문자열)
a, b = int(a), int(b)
print(a + b)             # 30

별표(*) 확장 언패킹 — 나머지를 리스트로

head, *tail = [1, 2, 3, 4, 5]
print(head, tail)     # 1 [2, 3, 4, 5]

*rest, last = [1, 2, 3, 4, 5]
print(rest, last)     # [1, 2, 3, 4] 5

first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)   # 1 [2, 3, 4] 5

*는 한 줄에 하나만 쓸 수 있습니다(두 개면 SyntaxError). * 변수는 항상 리스트(원소 0개면 []).

명시적 줄 연속: 백슬래시

줄 끝에 백슬래시를 쓰면 다음 줄과 한 줄로 이어집니다. 백슬래시 뒤에 공백 한 칸이라도 있으면 SyntaxError.

total = 1 + 2 + 3 + \
        4 + 5 + 6
print(total)   # 21

암시적 줄 연속: 괄호 안

(), [], {} 안에서는 백슬래시 없이 자유롭게 줄을 바꿀 수 있습니다.

total = (1 + 2 + 3 +
         4 + 5 + 6)

nums = [1, 2, 3,
        4, 5, 6]

실무는 암시적 방식을 권장하지만 시험엔 백슬래시도 나오므로 둘 다 알아두세요.

실기는 "실행 결과 쓰기"가 핵심입니다. 아래는 답을 틀리기 쉬운 포인트 모음입니다.

  • print의 자동 공백: print('a=', 5)의 결과는 a= 5(등호와 5 사이 공백 한 칸). a=5로 쓰면 오답.
  • end 값 한 글자도 확인: end=""는 줄바꿈 제거, end=" "는 공백, end=","는 쉼표. 정답에 공백/줄바꿈이 있는지 반드시 확인.
  • 100 vs 100.0: type(100) == type(100.0)False. 정수와 실수는 다른 자료형.
  • "100"은 str: 따옴표로 감싸면 숫자처럼 보여도 문자열.
  • /는 항상 실수: 10 / 33.333..., 정수 몫은 10 // 3.
  • 슬라이싱·range 끝 미포함: s[1:4]는 인덱스 1·2·3만, range(5)는 0~4까지(5 제외).
  • 요소 1개 튜플: (100)은 int, (100,)이 tuple.
  • {}는 딕셔너리: 빈 집합은 set().
  • d.get(key)는 기본값 None: 빈도 카운팅은 d.get(ch, 0)처럼 0을 줘야 함(None + 1은 TypeError).
  • map()/range()/제너레이터는 그대로 print하면 객체 주소/범위 정보가 나옴. 값 목록은 list()로 감싸야 함.
  • all([])는 True, any([])는 False (직관과 반대).
  • sorted()는 새 리스트 반환, list.sort()는 원본 수정 후 None 반환.
  • 얕은 복사 m[:] 후 안쪽 리스트에 += 하면 원본도 함께 바뀜.
  • 인접 문자열 자동 결합은 리터럴/f-string에만 적용(변수엔 + 필요).