정보처리기사 실기 개념정리 목차

Python — 슬라이스와 가변 객체, 두 축이면 끝난다

Python은 매회 1문항이 기본이었지만 2026-1회에 사상 첫 3문항으로 뛰었다. 문항 수 대비 개념 범위가 가장 좁아 — 슬라이싱, 가변 객체(얕은 복사), dict/set — 정리 효율이 제일 좋은 언어다. 이 편의 코드는 전부 기출 복원이며 python3로 실행 검증했고, 헷갈리는 문항은 줄마다 변수 상태를 적는 추적 표로 풀이 과정을 다 펼쳐 놨다.


1. 슬라이싱 — 끝 인덱스 미포함, 음수 step은 끝에서 출발

s[시작:끝:step] — 세 자리 각각의 규칙만 알면 된다.

s[a:b]    a부터 b '직전'까지        ← 끝 미포함이 오답 1순위
s[a:]     a부터 끝까지
s[:b]     처음부터 b 직전까지
s[::2]    처음부터 2칸씩 (인덱스 0,2,4…)
s[1::2]   인덱스 1부터 2칸씩 (1,3,5…)
s[::-1]   전체 역순
s[::-2]   '마지막'에서 2칸씩 역방향  ← 시작점이 끝이라는 게 함정

1.1 음수 step 추적 (2026-1 → 9A7A5A3A1A)

lst = list(range(10))    # [0,1,2,3,4,5,6,7,8,9]
for c in lst[::-2]:
    print(c, end='A')
print()

[::-2]마지막 원소(9)에서 2칸씩 거꾸로:

방문 순서12345
원소97531
출력9A7A5A3A1A

9A7A5A3A1A. end='A'마지막 1 뒤에도 붙는다(그래서 A가 5개). 8부터 시작한다고 보면 오답(8A6A4A2A0A), A를 4개로 세도 오답.

1.2 문자열 슬라이스 (2026-2 → _THIISING)

str = "_THIS_IS_KIM_SPEAKING"
#      0123456789...
a = str[:4]     # 인덱스 0~3 → "_THI"  (4는 미포함!)
b = str[6:8]    # 6~7        → "IS"
c = str[18:]    # 18~끝      → "ING"
ans = a + b + c # "_THIISING"

1.3 필터 결합 (2026-1 → veDamuH)

z = ''.join(c for c in y[::-1] if c not in 'ong')
# y = "HumanDev" → [::-1] "veDnamuH" → 'o','n','g' 제거(여기선 n 하나) → "veDamuH"

체크 3연타 — ① 끝 인덱스 미포함인가 ② 음수 step이면 시작이 마지막 원소인가 ③ end=/필터(if c not in)가 마지막 원소·모든 문자에도 적용되는가.

인덱싱 기출(2024-1 → Seynaau): i[1]두 번째 글자, i[-1]은 마지막 글자. 초기값(str = "S")을 빠뜨리지 않는 것까지가 문제다.

꼬리질문 대비 — "s[5:2]는?"(시작>끝이고 step 양수면 빈 문자열 ''). "s[-3:]는?" (뒤에서 3글자). "step이 0이면?"(ValueError).


2. 가변 객체 — 별칭, 제자리 수정, 얕은 복사

Python에서 리스트·딕셔너리는 가변(mutable) 이고 참조로 전달된다. 이 두 가지가 함정의 근원이다.

2.1 in-place 수정은 원본을 바꾼다 (2024-3 → 3)

def func(lst):
    for i in range(len(lst) // 2):
        lst[i], lst[-i-1] = lst[-i-1], lst[i]   # 양끝을 맞바꿈 = 제자리 뒤집기
lst = [1,2,3,4,5,6]
func(lst)                                        # 반환값 없어도 lst는 [6,5,4,3,2,1]
print(sum(lst[::2]) - sum(lst[1::2]))            # (6+4+2) - (5+3+1) = 12 - 9 = 3

리스트는 참조로 전달돼 함수가 return하지 않아도 원소 수정이 호출부에 남는다. reverse(), sort(), append()in-place 메서드는 원본을 바꾸고 반환값은 None이다.

함정a = lst.sort() 하면 a는 None이다. 정렬된 리스트를 원하면 sorted(lst)(새 리스트 반환).

2.2 얕은 복사 — 2026-1회를 무너뜨린 문항 (→ 10)

def f(a):
    m = [ [x] for x in a ]    # [ [1], [2], [3], [4] ]
    b = m[:]                  # ★ 얕은 복사 — 겉 리스트만 새것, 내부 리스트는 공유
    for i in range(len(b) - 1):
        b[i+1] += b[i]        # += 는 내부 리스트를 '제자리 연장' → m에도 보인다
    return sum(len(x) for x in m)
print(f([1, 2, 3, 4]))

b = m[:]겉 리스트만 새로 만들고 b[k]는 여전히 m[k]같은 내부 리스트를 가리킨다. 그래서 b[i+1] += b[i](제자리 연장)가 m에 그대로 반영된다:

단계m의 상태비고
초기[ [1],[2],[3],[4] ]b[k]는 m[k]와 같은 객체
i=0[ [1],[2,1],[3],[4] ]b[1] += b[0] → m[1] 연장
i=1[ [1],[2,1],[3,2,1],[4] ]b[2] += b[1] → m[2] 연장
i=2[ [1],[2,1],[3,2,1],[4,3,2,1] ]b[3] += b[2] → m[3] 연장

내부 리스트 길이 = 1, 2, 3, 4 → 합 10.

세 겹의 함정 — ① m[:]는 깊은 복사가 아니다(내부 객체 공유). ② 리스트의 +=는 새 리스트 생성이 아니라 **extend(제자리 연장)**다. ③ 그래서 b를 통한 수정이 m으로 '새어 나온다'. 셋 중 하나만 놓쳐도 답이 4(공유 무시)나 20(둘 다 합산)이 된다.

구분코드내부 객체
별칭b = m전부 공유 (같은 리스트)
얕은 복사b = m[:], m.copy(), list(m)겉만 분리, 내부 공유
깊은 복사copy.deepcopy(m)전부 분리

꼬리질문 대비 — "b[i+1] = b[i+1] + b[i]였다면?"(이건 새 리스트를 만들어 b에만 재대입 → m은 안 바뀜 → 답이 달라짐). +=(제자리)와 = x +(재대입)의 차이가 이 문제의 핵심.


3. dict / set — 삽입 순서, 스냅샷, comprehension

3.1 items() 순회는 삽입 순서 (2026-2 → CNNLRPYT)

locations = {"NYC": "New York", "LON": "London", "PAR": "Paris", "TKY": "Tokyo"}
result = ""
for key, location in locations.items():
    result += key[-1] + location[0]
항목key[-1]location[0]누적
NYC:New YorkCNCN
LON:LondonNLCNNL
PAR:ParisRPCNNLRP
TKY:TokyoYTCNNLRPYT
  • 3.7부터 dict는 삽입 순서를 언어 명세로 보장한다. 실제 시험지가 "Python 3.x 이상"이라고만 써서 "순서 미보장이면 4! = 24가지 아니냐"는 이의신청이 속출했다 — 가답안은 삽입 순서 기준 CNNLRPYT. 학습 관점에선 "3.7+ = 삽입 순서" 로 정리.
  • key[-1](키 끝 글자) vs location[0](값 첫 글자) — 어느 쪽에 어떤 인덱스인지가 전부.

3.2 comprehension + 갱신 + set 교집합 (2025-2 → 2)

lst = [1, 2, 3]
dic = {x: x * 2 for x in lst}   # {1:2, 2:4, 3:6}
s = set(dic.values())            # {2, 4, 6}  ← 이 시점의 스냅샷
lst[0] = 99
dic[2] = 7
s.add(99)
print(len(s & set(dic.values())))
줄 실행 후dics
초기{1:2, 2:4, 3:6}{2,4,6}(스냅샷)
lst[0]=99{1:2, 2:4, 3:6} (불변 — dic은 이미 생성됨){2,4,6}
dic[2]=7{1:2, 2:7, 3:6}{2,4,6}
s.add(99){1:2, 2:7, 3:6}{2,4,6,99}

s(={2,4,6,99}) ∩ set(dic.values())(={2,7,6}) = {2, 6} → len 2.

시점이 전부다set(dic.values())는 그 줄이 실행되는 순간의 값을 복사(스냅샷)한다. 이후 dic이 바뀌어도 s는 안 변하고, lst를 바꿔도 이미 만든 dic은 안 변한다.

3.3 enumerate + sum/len (2025-3)

data = [ [3,5,2,4,1], [4,5,1], [4,4,1,5,4], [4,5] ]
result = {}
for index, lis in enumerate(data):
    result[index] = (sum(lis), len(lis))
# {0:(15,5), 1:(10,3), 2:(18,5), 3:(9,2)}

enumerate(data)(0, [3,5,2,4,1]), (1, [4,5,1])…을 준다. 튜플 안의 (합, 길이) 순서를 바꿔 쓰는 게 대표 오답 — 코드에서 sum이 먼저인지 len이 먼저인지 확인.

set 연산 참조& 교집합, | 합집합, - 차집합, ^ 대칭차. dict는 keys(), values(), items() / 없는 키 접근은 KeyError, .get(k)은 None.


4. type 비교 — float는 int가 아니다 (2024-3 → 45)

def test(v):
    if type(v) == type(""):    return len(v)    # 문자열
    elif type(v) == type(100): return 101       # int '만'
    else:                      return 20        # 나머지 전부

print(test("100.0") + test(100.0) + test((100.0, 200.0)))
인자타입걸리는 분기반환
"100.0"str첫 번째len=5 (다섯 글자)
100.0floatelse (int 아님!)20
(100.0,200.0)tupleelse20

합 5 + 20 + 20 = 45.

함정 2연발 — ① 100.0은 float라 type(100)(int)과 다르다. ② "100.0"은 문자열이니 len(다섯 글자)이지 숫자 변환이 아니다. 보너스: 실행 경로에 안 타는 분기의 리턴값(101이든 100이든)은 답에 영향 없다 — 복원본끼리 그 값이 달라도 당황할 필요 없는 이유.

주요 타입: int, float, str, bool, list, tuple, dict, set, NoneType. type(x) == type(y) 대신 isinstance(x, int)도 쓰지만 시험 코드는 보통 type() 비교다.


5. 문자열 처리 — 카운트, split/join, 필터

# 2024-2 → ab3 ca3 (슬라이딩 윈도 카운트)
for i in range(len(str)):
    sub = str[i:i+len(p)]     # 범위 넘어가면 예외 없이 짧게 잘림
    if sub == p: result += 1
# "abdcabcabca"에서 "ab" 3회(인덱스 0,4,7), "ca" 3회(3,6,9)
  • 슬라이스가 문자열 끝을 넘어도 예외 없이 짧게 잘린다(C의 버퍼 오버런과 다른 점).
  • split() 인자 없음 = 연속 공백 무시 + 양끝 공백 제거. 입력에 공백이 없으면 분할 자체가 안 일어나 ["HumanDev"] 한 조각(2026-1). "a,b".split(",")처럼 구분자 지정도 가능.
  • ''.join(리스트) = 구분자 없이 이어붙이기, ",".join(리스트) = 쉼표로.
  • 기타 자주 쓰이는 것: .upper()/.lower(), .replace(a,b), .find(x)(없으면 -1), .strip(), .startswith(), f"{x}"(f-string).

6. 클래스가 나와도 겁먹지 않는다 (2025-1 → 13)

nodes = [Node(i) for i in li]
for i in range(1, len(li)):
    nodes[(i - 1) // 2].children.append(nodes[i])   # 부모 = (i-1)//2 : 완전이진트리
# calc: level % 2 == 1 인 노드만 합산 → 레벨 1의 5+8 = 13 (레벨 0, 2는 제외)
  • 기출의 클래스는 대개 장식이고, 본질은 인덱스 계산이다. (i-1)//2가 부모 인덱스면 완전이진트리(자식은 2i+1, 2i+2) — 종이에 트리를 그려 레벨을 매기면 풀린다.
  • li = [3,5,8,12,15,18,21]이면 레벨0: 3(i0) / 레벨1: 5(i1),8(i2) / 레벨2: 12,15,18,21. 홀수 레벨(1)만 합산 → 5+8 = 13.
  • 클래스 속성(2026-2 locations)은 인스턴스에서 그대로 읽힌다 — obj.locations는 클래스에 정의된 그 dict.

7. Python 문법 빠른 참조 (코드 문항 어디서든 나올 수 있음)

  • 정수 나눗셈·나머지·거듭제곱: 7 // 2 = 3(몫), 7 % 2 = 1(나머지), 2 ** 3 = 8. 음수 나눗셈은 바닥(floor) 방향: -7 // 2 = -4(‑3.5를 내림), -7 % 2 = 1.
  • 조건 표현식(삼항): a if 조건 else b.
  • 불리언: True/False(대문자), and/or/not(키워드), 0·""·[]·None은 거짓.
  • range: range(n) = 0..n-1, range(a,b) = a..b-1, range(a,b,step).
  • comprehension: [x*2 for x in a if x>0](리스트), {k:v for ...}(딕셔너리), {x for ...}(셋), (x for ...)(제너레이터).
  • 다중 대입·스왑: a, b = b, a(임시변수 없이 교환 — 슬라이싱 뒤집기의 핵심).
  • 함수 인자: 기본값 def f(x=0), 가변 위치 *args, 가변 키워드 **kwargs.
  • 예외: try / except / else / finally. raise로 발생.

8. 고난도 함정 (자체 출제 hard 세트 대비)

기출엔 아직 안 나왔지만 나올 수 있는 Python 함정. 각 항목은 hard 세트의 실제 문항이다.

8.1 가변 기본 인자 — 호출 간 공유

def add(x, acc=[]):
    acc.append(x)
    return sum(acc)
add(1); add(2); add(3)   # acc가 공유되어 1 → 3 → 6

기본값 []는 함수 정의 시 딱 한 번 만들어져 모든 호출이 같은 리스트를 공유한다. 매 호출마다 새로 [] 되지 않는다. 이걸 피하려면 def add(x, acc=None): acc = acc or []. §2의 얕은 복사와 더불어 "가변 객체 공유"의 대표 함정.

8.2 클로저 late binding — 변수를 값이 아니라 참조로 캡처

fs = [lambda: i for i in range(4)]
[f() for f in fs]   # [3, 3, 3, 3] — 넷 다 마지막 i(3)

람다는 i의 '값'이 아니라 '변수 i 자체'를 캡처한다. 나중에 호출할 때 i는 루프가 끝난 값 (3)이라 전부 3. 각 시점 값을 붙들려면 기본 인자로 즉시 캡처: lambda i=i: i → [0,1,2,3].

8.3 dict.fromkeys — 모든 키가 한 객체 공유

d = dict.fromkeys(['a', 'b'], [])   # a·b가 같은 리스트!
d['a'].append(1)                     # d['b']에도 [1]

fromkeys의 기본값도 한 번만 평가되어 모든 키가 같은 가변 객체를 공유한다. 키마다 독립 리스트가 필요하면 {k: [] for k in keys}(컴프리헨션은 매번 새로 만든다).

8.4 divmod로 진법 변환

n = 100; r = ''
while n:
    n, rem = divmod(n, 3)   # 몫·나머지 동시에
    r = str(rem) + r        # 나머지를 '앞에' 붙여야 자리가 맞음
# 100 → '10201' (3진법)

divmod(n, b)(n//b, n%b). 나머지를 앞에 붙여야(뒤가 아니라) 자릿값이 올바르게 쌓인다. 뒤에 붙이면 뒤집힌 답이 나온다.


9. 시험장 체크리스트

  1. 슬라이스: 끝 미포함 / 음수 step은 끝에서 출발 / step 2는 건너뛰기.
  2. [:]는 얕은 복사, +=는 제자리 연장 — "새어 나오는 수정"을 추적.
  3. in-place 메서드(reverse/sort/append)는 반환값이 아니라 원본을 바꾸고 None을 반환.
  4. dict는 삽입 순서(3.7+), set(...)은 그 순간의 스냅샷.
  5. type(100)type(100.0)type("100").
  6. //·%의 음수 방향, ** 거듭제곱, a,b=b,a 스왑을 헷갈리지 말 것.
  7. 각 줄 실행 후 컬렉션 내용을 옆에 적는다 — Python은 상태 추적이 곧 풀이다.

출처

  • 2024-1 ~ 2026-2 실기 복원 기출 (이 트랙 수록 8회차) — 기사퍼스트·두목넷·수제비(공개 미러)·뉴비티·chobopark 교차 검증
  • 본문 코드·추적 표 전량 python3(3.14) 실행 검증 완료 (얕은 복사 단계별 상태, dict 스냅샷, 슬라이스 결과 일치 확인)
  • dict 순서 보장: Python 3.7 릴리스 노트 (언어 명세 편입)
Java — 바인딩 규칙SQL·관계대수