파이썬의 모든 연산자 우선순위와 평가 규칙을 이해하고, 연산자 오버로딩의 원리를 파악한다.
🎯 이 강의에서 배우는 것
파이썬에는 수십 개의 연산자가 있습니다. 이들이 어떤 순서로 평가되는지(우선순위), 같은 우선순위에서 왼쪽부터 평가되는지 오른쪽부터 평가되는지(결합성)를 이해하지 못하면 미묘한 버그가 발생합니다. 이 강의에서는 연산자 우선순위 전체를 체계적으로 정리하고, 증강 할당 연산자와 연산자 오버로딩의 원리까지 이해합니다.
📊 연산자 우선순위 전체 테이블
아래 표에서 위로 갈수록 우선순위가 낮습니다 (나중에 평가됨):
| 우선순위 | 연산자 | 설명 |
|---|---|---|
| 1 (최저) | lambda | 람다 표현식 |
| 2 | if – else | 조건 표현식(삼항 연산자) |
| 3 | or | 논리 OR |
| 4 | and | 논리 AND |
| 5 | not x | 논리 NOT |
| 6 | in, not in, is, is not, <, <=, >, >=, ==, != | 비교, 멤버십, 동일성 |
| 7 | | | 비트 OR |
| 8 | ^ | 비트 XOR |
| 9 | & | 비트 AND |
| 10 | <<, >> | 비트 시프트 |
| 11 | +, - | 덧셈, 뺄셈 |
| 12 | *, @, /, //, % | 곱셈, 나눗셈 |
| 13 | +x, -x, ~x | 단항 연산자 |
| 14 | ** | 거듭제곱 |
| 15 (최고) | x[i], x[i:j], x.attr, f() | 인덱싱, 슬라이싱, 속성, 호출 |
# 우선순위 실험
print(2 + 3 * 4) # 14 (곱셈 먼저)
print((2 + 3) * 4) # 20 (괄호로 우선순위 변경)
print(True or False and False) # True
# and가 or보다 우선: True or (False and False) = True or False = True
print(not True or True) # True
# not이 or보다 우선: (not True) or True = False or True = True
print(-2 ** 2) # -4 (** 먼저, 단항 - 나중)
# ** 가 단항 - 보다 우선: -(2**2) = -4
print((-2) ** 2) # 4 (괄호로 우선순위 변경)
🔄 결합성 (Associativity)
같은 우선순위의 연산자가 연속으로 나올 때 어느 방향으로 묶을지를 결합성이 결정합니다:
# 왼쪽 결합 (대부분의 연산자)
print(10 - 3 - 2) # (10-3) - 2 = 5 (왼쪽에서 오른쪽)
print(10 / 2 / 5) # (10/2) / 5 = 1.0
# 오른쪽 결합 (거듭제곱 ** 와 단항 연산자)
print(2 ** 3 ** 2) # 2 ** (3**2) = 2**9 = 512 (오른쪽에서 왼쪽!)
print(2 ** (3 ** 2)) # 512 (같은 결과)
print((2 ** 3) ** 2) # 64 (다른 결과)
# 수학에서 a^b^c = a^(b^c)와 동일한 관례
# 할당 연산자도 오른쪽 결합
a = b = c = 5 # c=5, b=5, a=5 순서로 처리
🔗 체인 비교의 정확한 동작
x = 5
# 체인 비교
print(1 < x < 10) # True — 수학적 표현과 동일
# 내부적으로: (1 < x) and (x < 10)
# 단, x는 한 번만 평가됨
# 함수 호출이 포함된 체인에서 중요
call_count = 0
def get_val():
global call_count
call_count += 1
return 5
call_count = 0
result = 1 < get_val() < 10 # get_val() 한 번만 호출
print(call_count, result) # 1 True
call_count = 0
result = (1 < get_val()) and (get_val() < 10) # 두 번 호출
print(call_count, result) # 2 True
# 다양한 체인 비교
a = b = c = 5
print(a == b == c) # True
print(a <= b <= c) # True
print(a != b) # False
# 주의: 타입이 다르면 예상치 못한 결과
print(0 == False == 0.0) # True (0 == False 그리고 False == 0.0)
📝 증강 할당 연산자와 가변/불변 차이
+=, -= 등의 증강 할당 연산자는 불변/가변 객체에서 다르게 동작합니다:
# 불변 객체 (int, str, tuple): 새 객체 생성
x = 5
print(id(x)) # 예: 1000
x += 3 # 새 정수 8 객체 생성, x가 가리키는 곳 변경
print(id(x)) # 다른 주소 (새 객체)
# 가변 객체 (list): in-place 수정
lst = [1, 2, 3]
print(id(lst)) # 예: 2000
lst += [4, 5] # __iadd__ 호출 → 같은 리스트 객체를 수정
print(id(lst)) # 같은 주소! (같은 객체)
print(lst) # [1, 2, 3, 4, 5]
# 이 차이가 실수로 이어지는 경우
a = [1, 2, 3]
b = a
a += [4, 5] # a가 가리키는 리스트를 in-place로 수정
print(b) # [1, 2, 3, 4, 5] — b도 변경됨!
# vs
a = [1, 2, 3]
b = a
a = a + [4, 5] # 새 리스트 생성 후 a에 할당
print(b) # [1, 2, 3] — b는 원래 리스트 유지
🎭 연산자 오버로딩 미리보기
파이썬의 연산자는 내부적으로 특수 메서드(dunder method)를 호출합니다. 이것이 1 + 2와 "a" + "b"가 모두 +로 동작하는 이유입니다:
# + 연산자의 내부 동작
a = 1
b = 2
print(a + b) # 3
print(a.__add__(b)) # 3 (내부적으로 이것을 호출)
s1 = "hello"
s2 = " world"
print(s1 + s2) # "hello world"
print(s1.__add__(s2)) # "hello world" (str의 __add__)
# 내장 len()도 특수 메서드 호출
lst = [1, 2, 3]
print(len(lst)) # 3
print(lst.__len__()) # 3 (내부적으로 이것을 호출)
# 모든 연산자 → 특수 메서드 매핑
# + → __add__ / __radd__
# - → __sub__
# * → __mul__
# == → __eq__
# < → __lt__
# [] → __getitem__
# in → __contains__
# 커스텀 클래스에서 이 메서드들을 구현하면
# 자신만의 연산자 동작을 정의할 수 있음 (35강에서 자세히 다룸)
💡 괄호 활용 원칙
우선순위를 외우는 것보다 더 중요한 것은 코드 가독성입니다:
# 모호한 코드 (우선순위 암기에 의존)
result = a + b * c ** 2 - d and e or f
# 명확한 코드 (괄호로 의도를 표현)
result = ((a + (b * (c ** 2)) - d) and e) or f
# 실무 원칙:
# 1. 산술 연산과 비교 연산이 섞이면 괄호 사용
# 2. 논리 연산자 and, or, not을 비트 연산자와 섞으면 반드시 괄호
# 3. 확실하지 않으면 괄호를 추가하는 것이 안전
# 불필요한 괄호가 때로는 의도를 명확히 함
if (x > 0) and (y > 0): # 괄호 없어도 작동하지만 더 읽기 쉬움
print("1사분면")
⚠️ 자주 하는 실수
-2 ** 2가 4가 될 것이라 예상:**가 단항-보다 우선순위가 높아-(2**2) = -4입니다.(-2) ** 2 = 4를 원하면 괄호가 필요합니다.and와&를 혼동:and는 논리 연산자(단락 평가, 원래 값 반환),&는 비트 연산자(항상 두 피연산자 평가, 정수 반환). 불리언에서도 미묘하게 다릅니다.- 리스트에서
+=와+의 차이 모름:lst += [4]는 in-place(공유 참조에 영향),lst = lst + [4]는 새 객체 생성.
📝 정리 및 다음 강의 예고
- 연산자 우선순위: 산술 > 비교 > 논리 순서 (괄호로 항상 명확히 할 수 있음).
- 대부분은 왼쪽 결합, 거듭제곱
**만 오른쪽 결합. +=는 불변 객체에서 새 객체 생성, 가변 객체에서 in-place 수정.- 파이썬의 모든 연산자는 내부적으로 특수 메서드(dunder)를 호출합니다.
다음 강의: f-string과 모든 문자열 포맷팅 방법을 마스터합니다. % 연산자, str.format(), f-string의 역사와 차이, 그리고 f-string의 모든 형식 지정자를 배웁니다.
관련 주제
- 연산자 우선순위 테이블
- 결합성(오른쪽 결합 **)
- 체인 비교 평가
- 증강 할당(+=)
- __iadd__
- 연산자 오버로딩 개념
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 파이썬 기초 40강 — 처음 배우는 프로그래밍
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
댓글
0/1000
불러오는 중...
