1. 논리 연산
참(True), 거짓(False) 값을 다루는 연산 → 조건문, if문에서 비교 연산자(==, !=, <, > 등)를 쓴 판단을 연결하는데에 자주 쓰임
- bool뿐 아니라 bool 값으로 해석될 수 있는 값들(Truthy, Falsy) 도 사용 가능
- 전체 값 자체 가 참인지 거짓인지 평가
- 항상 불리언 결과를 반환하는 게 아니라,
Python (and, or) 에서는 참/거짓을 판단해서 피연산자 자체 (= 연산에 사용된 원래 값 중 하나를 그대로)를 반환함 - 단축 평가 ⭕(and, or)
※ Falsy한 값 (False로 간주되는 값들)
- None : 아무 값도 없음
- False : 불리언 False
- 0, 0.0 : 숫자 0
- "" : 빈 문자열
- [], {}, (), set(), range(0) : 빈 리스트, 빈 딕셔너리, 빈 튜플, 빈 집합, 빈 range 객체
- 그외 모든 값들은 Truthy
※ Truthy / Falsy란?
- Truthy : 조건문에서 참(True)으로 평가되는 값
- Falsy : 조건문에서 거짓(False)으로 평가되는 값
즉, bool(x)를 했을 때True가 되면 Truthy False가 되면 Falsy
※ 단축 평가 (Short-circuit)
- a or b에서 a가 True면 b는 평가하지 않음
- a and b에서 a가 False면 b는 평가하지 않음
즉, 첫 번째 값으로 결과가 정해지면 두 번째 평가 생략
※ 논리 연산자는 항상 True/False를 반환하지 않음
Python의 논리 연산자는 True나 False 대신, 왼쪽 또는 오른쪽 값 자체를 그대로 반환함
print("A" and "B") # B 출력
print("" and "B") # "" 출력
print("A" or "B") # A 출력
print("" or "B") # B 출력
- and : 왼쪽 값이 Falsy면 그 값을 반환, 그렇지 않으면 오른쪽 값을 반환
- or : 왼쪽 값이 Truthy면 그 값을 반환, 그렇지 않으면 오른쪽 값을 반환
- 논리 연산자 (Logical Operators)
- and (논리곱, ∧) : 둘 다 True여야 결과가 True
- or (논리합, ∨) : 하나라도 True면 True
- not (논리 부정, ¬) : 반대값 (참 → 거짓, 거짓 → 참)
# 예시
a, b = True, False
print(a and b) # False
print(a or b) # True
print(not a) # False
※ 논리 XOR 은 직접 지원 X
- Python에서 논리값 (True, False) 에 대해 XOR 이라는 논리 연산자가 별도로 존재하지 않음
- 하지만 ^ 연산자는 지원
→ Python은 bool도 int 처럼 취급하기 떄문에 ^ 연산자(비트 XOR)를 사용하여 논리 XOR처럼 작동 가능
2. 비트 연산
숫자를 2진수(0과 1)로 바꿔서, 그 비트 단위로 계산하는 방식
- 정수의 비트 단위 정수형(int) 값만 정상 동작
- 각 정수 값을 이진수 비트 단위로 비교
- 비트 연산 결과, 정수(int) 를 반환
- 단축 평가 지원 ❌, 양쪽 모두 평가함
- 비트 연산자 (Bitwise Operators)
- & : 비트 AND → 둘 다 1일 때만 1
- | : 비트 OR → 하나라도 1이면 1
- ^ : 비트 XOR (배타적 논리합, ⊕) → 서로 다르면 1
- ~ : 비트 NOT → 정수 비트 반전 (0 → 1, 1 → 0)
- <<, >> : 비트를 왼쪽/오른쪽으로 밀기 (곱셈/나눗셈 효과)
# 예시 (5: 0b0101, 3: 0b0011)
print(5 & 3) # 1 (0b0001)
print(5 | 3) # 7 (0b0111)
print(5 ^ 3) # 6 (0b0110)
print(~5) # -6 (2의 보수 개념)
※ 왜 곱셉, 나눗셈 효과가 나는지?
- 이진수에서 왼쪽으로 한 칸 이동 = 2를 곱하는 것
→ x << n (= x × 2ⁿ) (2의 거듭제곱 곱셈)
- 이진수에서 오른쪽으로 한 칸 이동 = 2로 나누는 것(몫만)
→ x >> n (= x // 2ⁿ) (정수 나눗셈)
- 연산자별 의미 비교 (정수 vs 집합 vs 논리형)
- 구현된 위치는 비트연산자 오버로딩(__or__, __and__)
→ 비트연산자 심볼을 오버로딩해서 자기 타입에 맞는 동작 정의 - int : 비트 단위 연산
- set : 원소 집합 연산
- dict : 키 기준 병합/교집합
- bool : 정수 연산처럼 동작, 결과는 bool 로 유지
→ bool은 사실상 int의 서브클래스 ex) issubclass(bool, int) / isinstance(True, int) # 둘다 True
| 연산 이름 | int (정수) | set (집합) | bool (논리형) | |
| | | | | OR | 비트 OR (합집합) | 합집합 |
| & | AND | 비트 AND | 교집합 | 논리 AND (자동 bool 변환) |
| ^ | XOR | 비트 XOR | 대칭차집합 (공통 원소 제외) | 논리 XOR (직접 지원 X, != 사용) |
| ~ | NOT | 비트 반전 | (없음) | (없음) |
- Python int 와 비트 연산
- 정수의 동작 : 수학적 정수 연산(덧셈, 곱셈 등)
- Python int 는 내부적으로 필요한 만큼 워드(64비트 덩어리) 배열을 붙여서 확장되는 비트 배열 (C/C++는 정수 크기 고정)
→ 임의 정밀도, 크기 제한 ❌(big integer) , 범위 넘어서도 자동 처리 - 비트 연산 : 무한 크기의 2진수 비트 배열에 대해 AND, OR, SHIFT 등을 적용하는 것
- ∴ Python int 는 가변 길이 비트 배열 처럼 취급 가능, 집합 표현할 때 비트마스크 로 쓰기 매우 편리
→ 실제로는 워드 단위 비트 연산을 반복해서 수행, 사용자 입장에서는 그냥 무한 정밀 비트 연산
→ n=100까지도 한 정수 안에 다 들어감
→ 비트 연산은 int 비트셋(Python int를 비트셋처럼 씀)을 이용할 때 상수 시간(O(1))에 가깝게 처리 - 원소 번호 = 비트 위치로 고정
→ 연속적이고 작은 범위에서만 효율적
(예시) 1, 2, 1000 → 1000번째 비트까지 확보 필요 - list[bool], set 도 가능
→ int 비트마스크보다 느림 (직관적, for 루프 돌면서 OR 처리 O(n) / 오버헤드 + N이 작을 때 훨씬 느림 O(len(a) + len(b)) )
→ set의 경우, 원소가 흩어져 있어도 처리 가능 + 범위가 커도 무방 - 외부라이브러리 bitarray / numpy 배열 구현 가능
→ 내부적으로는 C 구현, 빠름, 대규모
※ 비트마스크 장점
집합 다루는 연산이 모두 한 CPU 명령(비트 연산)으로 끝남
- mask |= (1 << k) : 원소 추가 O(1)
- mask & (1 << k) : 원소 검사 O(1)
- mask1 | mask2 : 합집합 O(1) (기존 집합 연산은 O(n) )
- n=100일 때 set도 느리진 않지만 , 코드 간결 + 매우 빠름 (상수 시간)
→ 특히 C/C++ 에선 워드 크기가 정해져있어서 n=64 이하일때 한번의 연산으로 모든 집합 연산 가능
3. 자료형별 지원하는 연산
- 연산자 지원 여부 요약
- set은 Python 내부적으로 비트 연산자들을 오버로딩(overloading)해서 기호 연산을 집합 연산으로 바꿔놓았기 때문
→ a | b 는 내부적으로 a.__or__(b) 가 호출되고, 이는 곧 a.union(b) 와 같아짐 (merge 연산자)
| 연산자 | 지원 여부 | |
| set, frozenset | |(합집합), &(교집합), ^(대칭 차집합), -(차집합), <=(부분 집합), <(진부분집합), >= (상위 집합), >(진상위 집합), ==, != (집합 동등성 비교), in, not in (원소 포함 여부 |
합집합, 교집합, 차집합, 대칭 차집합 등 집합 연산 수학적 개념(중복 X, 순서 X)에 따라 {1, 2} = {2, 1} 산술 +, * 등은 미지원 |
| list, tuple, str | +(덧셈), *(반복), ==, !=(비교), <, <=, >, >= (사전식 비교), in, not in (포함 여부) |
+(연결/병합), *(반복), 비교, in 지원, 집합/비트 연산자 불가 |
| dict | ==, != (비교), in, not in (키 포함 여부) (수학/비트/집합 연산 X) |
==, in, |, &, |=(Python 3.9+) 지원 +, - 등은 미지원 |
| int | +, -, *, /, //, %, ** (사칙 연산) |, &, ^, ~ (집합 X, 비트 연산으로 작동), ==, !=, <, >, <=, >= (비교) and, or, not (논리 연산), in, not in (포함 여부) |
산술 연산자 및 비트 연산자 지원 집합 연산자 없음 |
| bool | and, or, not (논리연산), &, |, ^ (비트 연산), ==, != (비교), +, - (산술 연산, int처럼 동작), in, not in (포함 여부) |
논리 연산자, 비트 연산자 지원 (비트형 논리 연산) → 비트 연산 우선 순위가 낮음 + 논리 연산과의 혼동 주의 필요 집합 연산자 없음 |
- dict의 병합 연산 (3.9+)
from functools import reduce
# reduce + | 연산자
dicts = [{"a": 1}, {"b": 2}, {"c": 3}]
merged = reduce(lambda x, y: x | y, dicts)
print(merged) # {'a': 1, 'b': 2, 'c': 3}
# | 연산자 (3.9+, update() 도 동일 기능)
a = {"x": 1, "y": 2}
b = {"y": 100, "z": 200}
c = a | b # 병합 결과를 새 dict로 반환
print(c) # {'x': 1, 'y': 100, 'z': 200}
# ➕ unpacking 방식 (3.5 ~ 3.8)
c = {**a, **b}
print(c) # {'x': 1, 'y': 100, 'z': 200}
※ dict와 집합 연산
- 요약 : 키 집합에 대한 연산 을 하고, 값은 좌/우 dict 중 하나의 값을 취하는 규칙이 따름
- 3.9+ 부터 | , & 연산을 지원하지만 집합 연산은 아님
- 키를 기준으로 합치거나 교집합을 취하는 것 (값까지 집합 연산하는 것 X)
→ 집합 연산자를 빌려쓴 것 (집합 연산의 직관을 일부 반영)
- a | b : 새로운 dict 반환, 합집합처럼 키를 합치되 + 키 충돌 시 오른쪽(b) 값 우선
- a & b : 공통된 키만 유, 값은 왼쪽(a)의 값
'Language & Tool > Python' 카테고리의 다른 글
| 🐍얕은 복사, 깊은 복사 비교 (1) | 2025.07.01 |
|---|---|
| 🐍반복 처리를 위한 itertools, more_itertools (1) | 2025.06.20 |
| 🐍내부에서 작동하는 이중 밑줄 메서드 (+ 네임 맹글링, 단일 밑줄) (1) | 2025.06.17 |
| 🐍정밀 소수점 제어 모듈, decimal (1) | 2025.06.17 |
| 🐍배열, 행렬 기반의 numpy (0) | 2025.06.17 |