def calc(a, b): try: print("try") r = a / b except ZeroDivisionError: print("except") else: print("else") finally: print("finally")calc(9, 3)
try 와 else 와 except
try 와 else 와 finally
try 와 except 와 else
try 와 except 와 finally
정답B. try 와 else 와 finally
9 / 3 은 예외를 내지 않으므로 except로 들어가지 않는다. 예외가 일어나지 않았을 때만 실행되는 것이 else절이므로 try 다음에 else가 실행된다. finally절은 예외 유무와 관계없이 반드시 실행되므로 마지막에 돈다. 따라서 try, else, finally의 3줄이 된다. except가 나오는 선택지는 예외가 일어났다고 오해한 경우의 나열로, 이 호출에서는 ZeroDivisionError가 발생하지 않는다. finally가 빠진 선택지는 finally를 '예외가 일어났을 때의 뒤처리'로 혼동한 경우의 나열이다.
문제 2 | else절
try문의 else절이 실행되는 조건으로 옳은 것은 어느 것인가?
예외 발생 여부와 관계없이 항상 마지막에 실행된다
try절에서 예외가 발생했을 때만 실행된다
어느 except절과도 일치하지 않았을 때 실행된다
try절에서 예외가 발생하지 않았을 때만 실행된다
정답D. try절에서 예외가 발생하지 않았을 때만 실행된다
try문의 else절은 try절이 끝까지 예외를 내지 않고 끝났을 때만 실행된다. if문의 else가 '조건이 거짓일 때'인 것과 달리 try문의 else는 '잘 되었을 때' 쪽에 붙으므로 방향이 반대라고 기억해 두면 좋다. 예외가 발생했을 때 실행되는 것은 except절이다. 예외 유무와 관계없이 반드시 실행되는 것은 finally절 쪽이다. 어느 except와도 일치하지 않은 경우는 else로 떨어지는 것이 아니라 그 예외가 호출한 쪽으로 전달되어 간다.
f() 는 반환할 값을 준비한 뒤 실제로 반환되기 전에 finally절을 실행한다. 그 때문에 F가 먼저 표시되고 반환값은 T인 채로 있다. g() 는 finally절 안에 return이 있어 이것이 try절의 return을 덮어쓰므로 반환값은 N이 된다. print는 f() 와 g() 를 평가한 뒤 한 줄로 모아 표시하므로 F 줄이 먼저, T N 줄이 나중이라는 순서가 된다. T T가 되는 선택지는 finally의 return이 무시된다고 생각한 경우, F가 나중이 되는 선택지는 finally가 return 뒤에 돈다고 생각한 경우의 답이다.
문제 4 | 포착할 수 없는 예외
except Exception 이라고 쓴 except절로 포착할 수 없는 예외는 어느 것인가?
FileNotFoundError
ZeroDivisionError
KeyboardInterrupt
ModuleNotFoundError
정답C. KeyboardInterrupt
KeyboardInterrupt는 BaseException 바로 아래에 있으며 Exception을 상속하지 않는다. issubclass(KeyboardInterrupt, Exception) 은 False가 된다. 이용자가 Ctrl-C로 멈추려 했을 때의 것이므로 넓게 쓴 except Exception에 무심코 삼켜지지 않도록 계층이 나뉘어 있다. SystemExit도 같은 위치에 있다. 나머지 세 개는 모두 Exception의 자손으로, ZeroDivisionError는 ArithmeticError 아래, FileNotFoundError는 OSError 아래, ModuleNotFoundError는 ImportError 아래에 있으므로 except Exception으로 포착할 수 있다.
문제 5 | 예외의 부모
IndexError와 KeyError에 공통되는 가장 가까운 부모 클래스는 어느 것인가?
ReferenceError
ArithmeticError
AttributeError
LookupError
정답D. LookupError
IndexError도 KeyError도 첨자나 키로 조회했지만 찾지 못했다는 같은 성질을 가지므로 LookupError라는 공통의 부모 아래에 놓여 있다. except LookupError 라고 쓰면 리스트의 범위 밖 접근과 딕셔너리의 존재하지 않는 키를 모두 함께 받을 수 있다. ArithmeticError는 ZeroDivisionError 등의 부모로 계산 자체가 성립하지 않는 상황을 위한 것이다. AttributeError는 속성을 찾을 수 없을 때, ReferenceError는 약한 참조의 참조 대상이 사라졌을 때의 예외로, 둘 다 Exception 바로 아래에 있으며 LookupError와는 다른 계통이다.
문제 6 | except의 순서
다음 코드를 실행했을 때의 출력으로 옳은 것은 어느 것인가?
try: x = 8 / 0except ArithmeticError: print("A")except ZeroDivisionError: print("Z")
아무것도 출력되지 않고 정지한다
Z만 출력된다
A와 Z가 출력된다
A만 출력된다
정답D. A만 출력된다
8 / 0 은 ZeroDivisionError를 발생시키는데 ZeroDivisionError는 ArithmeticError의 자식 클래스이다. except절은 위에서부터 순서대로 대조해 처음 일치한 곳에서 끊으므로 먼저 쓴 except ArithmeticError가 잡아 버려 A만 출력된다. 뒤쪽의 except ZeroDivisionError에는 차례가 오지 않는다. 일치한 except는 하나만 실행되므로 A와 Z 둘 다 나오는 일은 없다. 좁은 것(자식 클래스)을 먼저, 넓은 것(부모 클래스)을 나중에 나열하는 것이 원칙인데, 이 순서를 반대로 한 것이 이 문제의 코드이다.
except 뒤에 소괄호로 묶은 튜플을 쓰면 그중 어느 것과 일치했을 때 실행된다. conv("21") 은 int("21") 이 21이 되므로 42를 반환한다. conv("abc") 는 문자열로는 넘길 수 있지만 숫자로 해석할 수 없으므로 ValueError가 된다. conv(None) 은 int() 에 넘길 수 있는 타입이 아니므로 TypeError가 된다. 타입은 맞지만 값이 부적절하면 ValueError, 애초에 타입이 맞지 않으면 TypeError라는 구분이 그대로 답의 나열이 된다.
문제 8 | 재발생
자체 정의한 예외와 인수 없는 raise를 사용한 다음 코드의 출력은 어느 것인가?
class LoadError(Exception): passdef load(): try: raise LoadError("bad") except LoadError: print("inner") raisetry: load()except Exception as e: print("outer", type(e).__name__)
inner만 나오고 outer는 출력되지 않는다
inner와 outer Exception 두 줄
inner와 outer LoadError 두 줄
inner 다음에 LoadError로 정지한다
정답C. inner와 outer LoadError 두 줄
except 안에서 인수 없이 raise라고만 쓰면 지금 처리 중인 예외를 그대로 호출한 쪽으로 재발생시킨다. 따라서 inner를 표시한 뒤 LoadError가 load() 밖으로 전달되어 바깥쪽의 except Exception이 받는다. LoadError는 Exception을 상속하고 있으므로 except Exception으로 포착할 수 있어 정지하지 않는다. type(e).__name__ 은 실제로 발생한 예외 클래스의 이름을 반환하므로 잡은 쪽의 타입 이름인 Exception이 아니라 LoadError가 표시된다. inner만 나오는 것은 재발생시키지 않고 그 자리에서 삼켰을 때의 출력이다.
첫 번째 호출은 리스트의 첨자 1이므로 2가 반환된다. 두 번째는 딕셔너리에 없는 키를 조회하므로 KeyError가 발생하는데 except에 쓰여 있는 것은 IndexError뿐이다. IndexError와 KeyError는 둘 다 LookupError의 자식이지만 서로 부모자식 관계가 아니므로 IndexError의 except로는 KeyError를 잡을 수 없다. 포착되지 않은 예외는 호출한 쪽으로 전달되고 받아 주는 이가 없으므로 프로그램이 정지한다. 둘 다 받고 싶다면 except LookupError 라고 쓰거나 튜플로 둘 다 나열한다.
문제 10 | assert
assert문에 대한 설명으로 옳은 것은 어느 것인가?
조건이 거짓이면 AssertionError 를 발생시키며 python3 -O 에서는 무효화된다
조건이 거짓이면 AssertionError 를 발생시키며 python3 -O 에서도 반드시 실행된다
조건이 참이면 AssertionError 를 발생시키며 python3 -O 에서는 무효화된다
조건이 거짓이면 ValueError 를 발생시키며 python3 -O 에서도 반드시 실행된다
정답A. 조건이 거짓이면 AssertionError 를 발생시키며 python3 -O 에서는 무효화된다
assert 조건, 메시지 는 조건이 거짓일 때 AssertionError를 발생시킨다. 조건이 참일 때는 아무 일도 일어나지 않는다. 중요한 것은 python3 -O 처럼 최적화 모드로 시작하면 assert문이 통째로 제거되어 한 줄도 실행되지 않는다는 점이다. 따라서 이용자의 입력을 검사할 목적으로 assert를 써서는 안 되며, 그런 용도에는 if로 조건을 보고 raise ValueError(...) 를 던진다. assert는 개발 중에 내부의 전제를 적어 두는 도구라고 역할을 구분해 기억해 두면 좋다.
문제 11 | self
메서드의 첫 번째 인수 self에 대한 설명으로 옳은 것은 어느 것인가?
부모 클래스의 속성을 참조하기 위해 예약된 이름이다
호출한 인스턴스 자신이 자동으로 전달되는 인수이다
인스턴스를 생성해 호출한 쪽으로 반환하기 위한 인수이다
클래스 그 자체가 자동으로 전달되는 예약된 이름이다
정답B. 호출한 인스턴스 자신이 자동으로 전달되는 인수이다
d.speak() 라고 쓰면 Python은 Dog.speak(d) 로 바꿔 읽어 호출한다. 이때 첫 번째 인수 자리에 들어가는 것이 호출한 인스턴스 자신이다. self라는 표기는 관습일 뿐이며 언어가 예약한 이름은 아니다. 첫 번째 인수가 자기 자신을 받는다는 위치만이 규칙이므로 Dog.speak(d) 라고 직접 써도 똑같이 동작한다. 인스턴스를 만들어 반환하는 것은 __new__ 와 클래스 호출 구조의 역할이며, __init__ 은 이미 만들어진 인스턴스를 초기화할 뿐이다. 부모 클래스 참조에는 super() 를 쓴다.
kind는 class 바로 아래에 쓰인 클래스 변수로, 그 클래스로 만들어진 모든 인스턴스가 같은 하나를 공유한다. a도 b도 자신의 __dict__ 에 kind를 가지고 있지 않으므로 속성을 읽을 때는 클래스 쪽까지 거슬러 올라가 같은 값을 본다. Dog.kind 에 대한 대입은 그 공유된 하나를 고쳐 쓰므로 이미 만들어져 있던 a와 b 양쪽에서 wolf가 보인다. 인스턴스 생성 시에 값이 복사되는 것이 아니므로 canine이 남는 선택지는 되지 않는다.
a.kind 에 대한 대입은 클래스 변수를 고쳐 쓰지 않고 a의 인스턴스 딕셔너리에 kind라는 새로운 항목을 만든다. 이후 a.kind 를 읽으면 이 새 항목이 먼저 발견되어 클래스 변수에 그림자를 드리우는 형태가 된다. b와 Dog.kind는 원래의 canine 그대로이다. 따라서 a.__dict__ 에는 __init__ 에서 넣은 name에 더해 kind도 나란히 있어 sorted하면 ['kind', 'name'] 이 된다. 전부 husky가 되는 선택지는 인스턴스에 대한 대입이 클래스 변수를 고쳐 쓴다고 오해한 경우, ['name'] 만 되는 선택지는 그림자가 생기지 않는다고 생각한 경우의 답이다.
self.items.append(x) 는 self.items 에 대한 대입이 아니라 self.items 로 도달한 객체를 호출하고 있을 뿐이다. p도 q도 자신의 items를 가지고 있지 않으므로 도달하는 곳은 클래스 변수인 같은 하나의 리스트가 된다. 결과적으로 p에서 더한 1도 q에서 더한 2도 같은 리스트에 들어가 어느 쪽에서 봐도 [1, 2] 가 된다. 인스턴스마다 별개의 그릇을 원한다면 __init__ 안에서 self.items = [] 라고 대입해 만든다. 그렇게 하면 [1] 과 [2] 로 나뉜다.
Cat의 __init__ 은 super().__init__(name) 을 호출하므로 부모인 Animal이 self.name 을 설정해 준다. 그 뒤 self.color 에 black을 넣으므로 두 속성이 모두 갖춰진다. cry는 자식에서 다시 정의했으므로 부모의 구현은 덮어써져 meow 쪽이 호출된다. isinstance(c, Animal) 은 Cat이 Animal을 상속하고 있는 이상 True가 된다. super() 를 빠뜨리면 name이 설정되지 않아 AttributeError가 되므로 None이 되는 선택지 같은 조용한 실패는 되지 않는다.
문제 16 | MRO
다중 상속한 다음 코드의 출력은 어느 것인가?
class A: def who(self): return "A"class B(A): def who(self): return "B"class C(A): def who(self): return "C"class D(B, C): passprint(D().who())print([k.__name__ for k in D.__mro__])
B 와 ['D', 'B', 'C', 'A', 'object']
C 와 ['D', 'C', 'B', 'A', 'object']
B 와 ['D', 'B', 'A', 'C', 'object']
A 와 ['D', 'B', 'C', 'A', 'object']
정답A. B 와 ['D', 'B', 'C', 'A', 'object']
메서드 해석 순서는 C3 선형화라는 규칙으로 정해지며 단순한 깊이 우선이 아니다. 공통 부모인 A는 A를 상속하고 있는 모든 자식보다 뒤에 놓이므로 D, B, C, A, object라는 나열이 된다. B로 들어가면 그대로 A로 가는 깊이 우선이라면 D, B, A, C, object가 되지만 그렇게 되지는 않는다는 점이 요주의이다. who() 는 MRO의 맨 앞부터 찾아 처음 발견된 것이 쓰이므로 B가 반환된다. class E(C, B) 라고 쓰면 E, C, B, A, object가 되어 먼저 쓴 쪽이 우선된다.
문제 17 | MRO의 모순
class B(A) 가 이미 정의되어 있을 때 class F(A, B): pass 라고 쓰면 무슨 일이 일어나는가?
정의는 되며 F().who() 는 A의 것이 호출된다
정의는 되며 __mro__ 는 F, A, B, object 가 된다
정의는 되며 F().who() 는 B의 것이 호출된다
클래스 정의 시점에 TypeError 가 된다
정답D. 클래스 정의 시점에 TypeError 가 된다
C3 선형화에는 기저 클래스를 나열한 순서를 지키는 것과 부모는 자식보다 뒤에 두는 것이라는 두 가지 규칙이 있다. F(A, B) 는 A를 B보다 먼저 두라는 지정인데, B는 A의 자식이므로 부모 A는 자식 B보다 뒤에 있어야 한다. 이 둘을 동시에 만족할 수 없으므로 일관된 MRO를 만들 수 없다고 보아 클래스 정의를 평가한 시점에 TypeError가 된다. 실행해 봐야 알 수 있는 것이 아니라 class 문을 통과한 순간 실패한다. 올바르게 쓰려면 class F(B, A) 처럼 자식을 먼저 쓴다.
밑줄 하나로 시작하는 _one은 관습적으로 내부용임을 나타낼 뿐 이름은 바뀌지 않아 밖에서 보통대로 읽을 수 있다. 밑줄 두 개로 시작하는 __two에는 이름 맹글링(name mangling)이 작동해 실제 속성 이름이 _P__two, 즉 _클래스명__이름 으로 바뀐다. 그 때문에 p.__two 라고 쓰면 AttributeError가 되지만 p._P__two 라고 쓰면 읽을 수 있다. __dict__ 에 나열되는 것도 맹글링된 뒤의 이름이므로 sorted하면 ['_P__two', '_one'] 이 된다. 맹글링되는 것은 밑줄 두 개로 시작하는 것뿐이며 _one은 대상이 아니다.
print(v) 와 str(v) 는 __str__ 을 쓰므로 V(1, 2) 가 된다. 한편 리스트나 딕셔너리 같은 컨테이너에 넣어 표시하면 각 요소에 대해 __repr__ 이 쓰이므로 [Vec(x=1, y=2)] 가 된다. 이 구분이 이 문제의 핵심이다. len(v) 는 __len__ 이 반환하는 2, v + Vec(3, 4) 는 __add__ 가 반환하는 새로운 Vec으로, 이를 print에 넘기면 다시 __str__ 이 쓰이므로 V(4, 6) 으로 표시된다. __str__ 이 정의되지 않은 클래스에서는 __repr__ 이 대신 쓰이지만, 반대로 __repr__ 대신 __str__ 이 쓰이는 일은 없다.
문제 20 | __eq__
__eq__ 만을 정의한 다음 코드의 출력은 어느 것인가?
class Point: def __init__(self, x): self.x = x def __eq__(self, other): return self.x == other.xp = Point(1)q = Point(1)print(p == q, p is q)print(Point.__hash__ is None)try: print(len({p, q}))except TypeError: print("TypeError")
True False 와 False 와 2
True False 와 True 와 TypeError
True False 와 False 와 1
True True 와 True 와 TypeError
정답B. True False 와 True 와 TypeError
__eq__ 를 정의했으므로 p == q 는 속성 x를 비교해 True가 되지만, p와 q는 별개로 만들어진 객체이므로 p is q 는 False인 채로 있다. 여기서 중요한 것은 __eq__ 를 정의한 클래스는 __hash__ 가 자동으로 None으로 설정된다는 점으로, Point.__hash__ is None 은 True가 된다. 해시를 가지지 않는 객체는 집합이나 딕셔너리의 키로 쓸 수 없으므로 {p, q} 를 만들려 하면 TypeError가 된다. 집합에도 넣고 싶다면 같을 때 같은 값을 반환하는 __hash__ 를 스스로 정의해야 한다.
math.sqrt 는 나누어떨어지는 경우에도 float를 반환하므로 16의 제곱근은 4가 아니라 4.0 으로 표시된다. math.floor 는 수직선의 아래 방향으로 내림하므로 -1.5는 -2가 되고, math.ceil 은 위 방향이므로 -1.5는 -1이 된다. 음수에서는 0 방향으로 내림하는 int(-1.5) 가 -1이 되는 것과 floor의 결과가 어긋난다는 점에 주의한다. floor와 ceil을 혼동하면 -1 -2 나열이 되고, sqrt의 반환값 타입을 int라고 착각하면 4가 된다.
문제 22 | os.path
os.path.splitext('/tmp/z.txt') 와 os.path.basename('/tmp/z.txt') 가 반환하는 값의 조합은 어느 것인가?
('/tmp/z', '.txt') 와 'z.txt'
('/tmp', 'z.txt') 와 '/tmp'
('/tmp/z', 'txt') 와 'z.txt'
('/tmp/z.txt', '') 와 'z.txt'
정답A. ('/tmp/z', '.txt') 와 'z.txt'
os.path.splitext 는 확장자만을 분리해 점을 확장자 쪽에 붙인 채 2요소 튜플을 반환한다. 따라서 ('/tmp/z', '.txt') 가 된다. 점이 빠져 'txt' 가 되는 일은 없다. os.path.basename 은 경로 끝의 이름을 반환하므로 'z.txt' 이다. 디렉터리 쪽인 '/tmp' 를 반환하는 것은 os.path.dirname 쪽이며, 둘을 함께 담은 튜플을 원할 때는 os.path.split 을 쓴다. splitext는 디렉터리 구분자가 아니라 확장자로 자른다고 역할을 구분해 기억해 두면 좋다.
re.match 는 문자열의 맨 앞부터만 매칭하므로 'abc' 의 맨 앞은 'a' 이며 'b' 에는 일치하지 않아 None을 반환한다. re.search 는 도중에도 찾으므로 'b' 에 일치해 .group() 으로 일치한 문자열 b를 꺼낼 수 있다. 이 차이가 re에서 가장 자주 묻는 부분이다. re.findall 은 패턴에 일치한 부분만을 모두 모은 리스트를 반환하며 \d+ 는 연속된 숫자이므로 ['1', '22', '333'] 이 된다. 구분 문자까지 포함되는 일은 없다. 일치가 없으면 빈 리스트를 반환한다.
date 객체를 그대로 print하면 ISO 형식의 하이픈 구분으로 2026-08-22 로 표시된다. 서식을 바꾸고 싶을 때 쓰는 것이 strftime으로 '%Y/%m/%d' 를 넘기면 슬래시 구분 문자열 2026/08/22 가 반환된다. date에 timedelta(days=10) 을 더하면 반환되는 것은 timedelta가 아니라 10일 뒤를 가리키는 새로운 date이므로 print하면 2026-09-01 로 표시된다. 월을 넘어가는 계산은 datetime 쪽이 처리해 주므로 일(day)의 숫자에 그냥 10을 더한 2026-08-32 같은 존재하지 않는 날짜는 되지 않는다. print(d) 가 strftime과 같은 서식이 된다고 생각하면 슬래시 구분이 나열되지만 기본 표시는 하이픈 구분이다. 날짜끼리 뺄셈을 하면 date가 아니라 timedelta가 반환되며 그 표시는 10 days, 0:00:00 같은 형태가 된다.
문제 25 | Counter
collections.Counter를 사용한 다음 코드의 출력은 어느 것인가?
from collections import Counterc = Counter("abracadabra")print(c.most_common(2))print(c["a"], c["z"])
[('a', 5), ('b', 2)] 와 5 와 KeyError
[('a', 5), ('r', 2)] 와 5 0
[('a', 5), ('b', 2)] 와 5 0
[('a', 5), ('b', 2), ('r', 2)] 와 5 0
정답C. [('a', 5), ('b', 2)] 와 5 0
'abracadabra' 에는 a가 5개, b와 r이 2개씩, c와 d가 1개씩 포함되어 있다. most_common(2) 는 많은 순으로 2개만을 요소와 개수의 튜플 리스트로 반환하므로 [('a', 5), ('b', 2)] 가 된다. 개수가 같은 b와 r은 먼저 나타난 쪽이 앞에 놓이므로 b가 채택된다. Counter는 딕셔너리의 일종이지만 포함되지 않은 요소를 조회해도 KeyError가 되지 않고 0을 반환한다는 점이 일반 딕셔너리와 다르다. 세어 본 뒤 존재 여부를 확인하지 않아도 되도록 하기 위한 설계이다.
namedtuple이 만드는 타입은 표시하면 타입 이름과 필드 이름이 붙은 Pt(x=1, y=2) 라는 형태가 된다. 값은 이름으로도 첨자로도 읽을 수 있으므로 p.x 와 p[0] 은 둘 다 1이다. 그리고 namedtuple은 튜플의 파생 타입이므로 isinstance(p, tuple) 은 True가 된다. 즉 튜플의 일종이며 요소를 나중에 고쳐 쓸 수는 없다. 고쳐 쓴 값을 가지게 하고 싶다면 값을 바꿔 넣은 새 인스턴스를 만드는 _replace를 쓰거나 일반 클래스를 정의한다.
문제 27 | 한 번 쓰고 마는 것
같은 map 객체를 두 번 list()에 넘긴 다음 코드의 출력은 어느 것인가?
m = map(str, [1, 2, 3])print(list(m))print(list(m))
1번째 줄은 ['1', '2', '3'], 2번째 줄은 None
1번째 줄은 ['1', '2', '3'], 2번째 줄은 []
1번째 줄은 [1, 2, 3], 2번째 줄은 []
1번째 줄도 2번째 줄도 ['1', '2', '3']
정답B. 1번째 줄은 ['1', '2', '3'], 2번째 줄은 []
map이 반환하는 것은 리스트가 아니라 이터레이터로, next() 를 부를 때마다 다음 값을 하나씩 내주고 다하면 그걸로 끝이다. 첫 번째 list() 에서 세 개를 모두 꺼내 버리므로 두 번째 list() 는 아무것도 남지 않은 빈 리스트가 된다. 오류도 None도 되지 않고 조용히 비어 버리므로 알아채기 어렵다. map(str, ...) 은 각 요소를 문자열로 만드므로 1번째 내용은 숫자가 아니라 ['1', '2', '3'] 이다. 같은 일이 filter zip enumerate와 제너레이터에서도 일어나므로 여러 번 돌리려면 받은 직후에 list() 로 굳혀 둔다.
filter의 첫 번째 인수에 None을 넘기면 요소 그 자체의 참거짓으로 걸러낸다. 0과 빈 문자열과 None은 거짓으로 평가되므로 떨어져 나가고 남는 것은 [1, 2, 'a'] 이다. 떨어진 쪽이 남는 것이 아니다. all은 거짓인 요소가 하나도 없으면 True를 반환하는 사양이므로 요소가 하나도 없는 빈 리스트에서는 True가 된다. any는 참인 요소가 하나라도 있으면 True이므로 비어 있으면 False이다. 빈 나열을 넘겼을 때 이 둘이 반대되는 답을 낸다는 점이 자주 출제된다.
yield를 포함한 함수를 호출해도 본체는 한 줄도 실행되지 않는다. 반환되는 것은 제너레이터 객체뿐이므로 먼저 created가 표시된다. 첫 번째 next(g) 에서 비로소 맨 앞부터 움직이기 시작해 start를 표시한 뒤 yield 1 에서 멈춰 1을 반환한다. 다음 next(g) 는 멈춘 지점부터 재개해 mid를 표시하고 yield 2 에서 멈춰 2를 반환한다. 이 지연 실행이 일반 함수와의 결정적인 차이로, 여기서 다시 next(g) 를 부르면 함수가 끝나 StopIteration이 된다.
문제 30 | is와 ==
is 연산자와 == 연산자를 나열한 다음 코드의 출력은 어느 것인가?
a = [1, 2]b = [1, 2]c = aprint(a == b, a is b)print(a == c, a is c)
True False 와 True False
True True 와 True True
False False 와 True True
True False 와 True True
정답D. True False 와 True True
==은 값이 같은지를 묻는 연산자이므로 내용이 같은 a와 b는 True가 된다. is는 같은 객체인지를 묻는 연산자로, a와 b는 별개로 만들어진 두 개의 리스트이므로 False이다. 한편 c = a 는 새로운 리스트를 만들지 않고 같은 객체에 다른 이름을 붙였을 뿐이므로 a is c 는 True가 된다. 즉 c를 통해 리스트를 고쳐 쓰면 a에서도 변화가 보인다. None은 하나만 존재하는 특별한 객체이므로 x is None 이라고 쓰는 것이 관례이지만, 값을 비교하고 싶을 때는 항상 ==을 쓴다.
연습: 이 페이지의 문제 풀기
무작위로 출제하는 연습 도구입니다(JavaScript가 활성화된 경우에 작동합니다). 위의 문제와 해설은 그대로 모두 읽을 수 있습니다.
※ 해설은 학습용 정보 제공입니다. 시험 출제 범위와 제도는 연도에 따라 바뀌므로, 반드시 시행 기관의 공식 발표를 확인하십시오.
이 페이지는 일본어 원문을 번역한 것입니다. 번역과 원문의 내용이 다를 경우 일본어판이 우선합니다. 일본어 원문 보기