1 개요
이전 글에서 파이썬의 특징과 개발환경 설정을 다루었다. 이 글에서는 프로그래밍의 가장 기본 단위인 변수와 데이터 타입을 다룬다.
프로그램은 결국 데이터를 입력받아 처리하고 결과를 출력하는 과정이다. 변수는 이 데이터를 저장하는 그릇이고, 데이터 타입은 그릇에 담을 수 있는 내용물의 종류를 결정한다. 파이썬은 이 그릇에 라벨만 붙이면 되고, 내용물의 종류는 파이썬이 알아서 판단한다. 이것이 동적 타이핑(dynamic typing)이다.
2 변수
2.1 변수란 무엇인가
변수(variable)는 값에 이름을 붙이는 것이다. 메모리에 저장된 데이터를 가리키는 이름표라고 이해하면 된다.
age는 정수 25가 저장된 메모리 위치를 가리키는 이름이다. C나 Java에서는 int age = 25;처럼 타입을 먼저 선언해야 하지만, 파이썬에서는 age = 25만으로 충분하다. 파이썬이 25를 보고 “이것은 정수다”라고 자동으로 판단한다.
2.2 동적 타이핑
파이썬의 가장 큰 특징 중 하나이다. 같은 변수에 서로 다른 타입의 값을 재할당할 수 있다.
C/Java에서 int x = 10; 후에 x = "hello";를 시도하면 컴파일 에러가 발생한다. 파이썬은 변수가 타입을 갖는 것이 아니라, 값(객체)이 타입을 갖기 때문에 이것이 가능하다. 변수는 단지 객체를 가리키는 이름표일 뿐이다.
이 유연함은 프로토타이핑을 빠르게 하지만, 대규모 프로젝트에서는 타입 혼동으로 인한 버그 원인이 되기도 한다. 이를 보완하기 위해 파이썬 3.5부터 타입 힌트(type hint)를 지원한다.
2.3 변수 명명 규칙
| 규칙 | 예시 (O) | 예시 (X) |
|---|---|---|
| 영문자, 숫자, 언더스코어 사용 | my_var, count2 |
my-var, 2count |
| 숫자로 시작 불가 | var1 |
1var |
| 예약어 사용 불가 | my_class |
class, for, if |
| 대소문자 구분 | Name과 name은 다른 변수 |
파이썬 커뮤니티는 snake_case(단어 사이에 언더스코어)를 관례로 사용한다. myVariable(camelCase)은 Java/JavaScript의 관례이다.
2.4 변수의 메모리 구조
파이썬에서 변수는 C처럼 “메모리 상자”가 아니라 “이름표(label)”이다. 이 차이는 중요하다.
b = a는 리스트를 복사하는 것이 아니라, 같은 객체에 이름표를 하나 더 붙이는 것이다. 이것을 이해하지 못하면 “내가 b만 바꿨는데 왜 a도 바뀌지?”라는 혼란에 빠진다. 독립적인 복사가 필요하면 b = a.copy() 또는 b = list(a)를 사용한다.
id() 함수로 객체의 메모리 주소를 확인할 수 있다.
3 기본 데이터 타입
파이썬의 기본 데이터 타입은 크게 숫자형, 문자열, 불리언, 컬렉션으로 나뉜다.
| 분류 | 타입 | 예시 | 변경 가능(mutable) |
|---|---|---|---|
| 숫자형 | int |
42, -7, 0b1010 |
X |
float |
3.14, -0.5, 1e-3 |
X | |
complex |
3+4j |
X | |
| 문자열 | str |
"hello", 'world' |
X |
| 불리언 | bool |
True, False |
X |
| 컬렉션 | list |
[1, 2, 3] |
O |
tuple |
(1, 2, 3) |
X | |
dict |
{"a": 1, "b": 2} |
O | |
set |
{1, 2, 3} |
O |
mutable/immutable 구분이 중요한 이유: immutable 객체는 값을 변경하면 새 객체가 생성되고, mutable 객체는 같은 객체가 제자리에서 변경된다. 앞서 본 리스트 예시(b = a 후 b.append(4))가 mutable이기 때문에 발생하는 현상이다.
3.1 숫자형
3.1.1 int (정수)
파이썬의 정수는 크기 제한이 없다. C의 int는 32비트(약 \(\pm 2 \times 10^9\) )로 제한되지만, 파이썬은 메모리가 허용하는 한 얼마든지 큰 수를 다룰 수 있다.
# 기본 연산
a = 17
b = 5
print(a + b) # 22 덧셈
print(a - b) # 12 뺄셈
print(a * b) # 85 곱셈
print(a / b) # 3.4 나눗셈 (항상 float 반환)
print(a // b) # 3 정수 나눗셈 (몫)
print(a % b) # 2 나머지
print(a ** b) # 1419857 거듭제곱
# 큰 수도 문제없다
big = 10 ** 100
print(type(big)) # <class 'int'>/ 연산은 항상 float를 반환한다는 점에 주의한다. 정수 결과가 필요하면 //를 사용한다. 이것은 Python 2에서 Python 3으로 넘어오면서 바뀐 동작이다.
진법 표현도 지원한다.
3.1.2 float (부동소수점)
부동소수점은 IEEE 754 표준을 따르며, 정밀도 한계가 있다.
이것은 파이썬의 버그가 아니라 2진수로 소수를 표현할 때 발생하는 근본적인 한계이다. 금융 계산처럼 정확한 소수 연산이 필요하면 decimal 모듈을 사용한다.
실무에서 float 비교가 필요할 때는 math.isclose()를 사용한다.
3.2 문자열 (str)
문자열은 작은따옴표('...') 또는 큰따옴표("...")로 생성한다. 여러 줄 문자열은 삼중따옴표("""...""")를 사용한다.
3.2.1 문자열 인덱싱과 슬라이싱
s = "Python"
print(s[0]) # 'P' — 0부터 시작
print(s[-1]) # 'n' — 뒤에서 첫 번째
print(s[1:4]) # 'yth' — 1번 이상 4번 미만
print(s[:3]) # 'Pyt' — 처음부터 3번 미만
print(s[3:]) # 'hon' — 3번부터 끝까지
print(s[::-1]) # 'nohtyP' — 역순문자열은 immutable이다. 특정 문자를 바꾸려면 새 문자열을 생성해야 한다.
3.2.2 주요 문자열 메서드
text = " Hello, World! "
text.strip() # "Hello, World!" 양쪽 공백 제거
text.lower() # " hello, world! " 소문자
text.upper() # " HELLO, WORLD! " 대문자
text.replace("World", "Python") # " Hello, Python! "
text.split(",") # [' Hello', ' World! ']
",".join(["a", "b"]) # "a,b"
# 검색
"Hello" in text # True
text.find("World") # 9 (인덱스 반환, 없으면 -1)
text.count("l") # 33.2.3 f-string (포맷 문자열)
파이썬 3.6부터 도입된 f-string은 가장 간결한 문자열 포맷팅 방법이다.
3.3 불리언 (bool)
True와 False 두 값만 가진다. 조건문과 논리 연산의 기반이다.
is_active = True
is_admin = False
# 논리 연산
print(is_active and is_admin) # False
print(is_active or is_admin) # True
print(not is_active) # False파이썬에서는 bool이 int의 하위 클래스이다. True는 1, False는 0으로 동작한다.
3.3.1 Truthy와 Falsy
파이썬에서는 bool이 아닌 값도 조건문에서 True 또는 False로 평가된다. 이것을 truthy/falsy라고 한다.
| Falsy (거짓으로 평가) | Truthy (참으로 평가) |
|---|---|
False |
True |
0, 0.0, 0j |
0이 아닌 모든 숫자 |
"" (빈 문자열) |
내용이 있는 문자열 |
[], (), {}, set() (빈 컬렉션) |
내용이 있는 컬렉션 |
None |
그 외 모든 객체 |
이 규칙을 활용하면 코드를 간결하게 작성할 수 있다.
빈 리스트는 falsy이므로 if items:만으로 “리스트에 내용이 있는가?”를 판단할 수 있다.
3.4 컬렉션 타입
3.4.1 list (리스트)
순서가 있고 변경 가능한(mutable) 컬렉션이다. 서로 다른 타입의 요소를 담을 수 있다.
fruits = ["apple", "banana", "cherry"]
# 접근
fruits[0] # "apple"
fruits[-1] # "cherry"
# 변경
fruits[1] = "blueberry"
fruits.append("date") # 끝에 추가
fruits.insert(0, "avocado") # 특정 위치에 삽입
fruits.remove("cherry") # 값으로 삭제
popped = fruits.pop() # 마지막 요소 제거 및 반환
# 정렬
numbers = [3, 1, 4, 1, 5]
numbers.sort() # 제자리 정렬: [1, 1, 3, 4, 5]
sorted_nums = sorted(numbers, reverse=True) # 새 리스트 반환3.4.2 리스트 컴프리헨션
파이썬의 강력한 기능 중 하나이다. 반복문과 조건문을 한 줄로 표현한다.
3.4.3 tuple (튜플)
순서가 있고 변경 불가능한(immutable) 컬렉션이다. 리스트와 비슷하지만 한 번 생성하면 수정할 수 없다.
point = (3, 4)
rgb = (255, 128, 0)
# 접근은 리스트와 동일
print(point[0]) # 3
# 수정 불가
# point[0] = 5 # TypeError 발생튜플을 사용하는 이유: (1) 변경되면 안 되는 데이터를 보호한다 (2) dict의 키로 사용할 수 있다 (list는 불가) (3) 리스트보다 메모리를 적게 사용하고 접근 속도가 빠르다.
3.4.4 dict (딕셔너리)
키-값 쌍으로 이루어진 컬렉션이다. 키를 통해 \(O(1)\) 시간에 값을 조회할 수 있다.
user = {
"name": "Alice",
"age": 25,
"scores": [95, 87, 92]
}
# 접근
user["name"] # "Alice"
user.get("email", "N/A") # "N/A" — 키가 없으면 기본값 반환
# 수정
user["age"] = 26
user["email"] = "alice@example.com" # 새 키-값 추가
# 삭제
del user["scores"]
# 순회
for key, value in user.items():
print(f"{key}: {value}")user["email"]처럼 없는 키에 접근하면 KeyError가 발생한다. 안전한 접근이 필요하면 .get() 메서드를 사용한다. 이 차이는 실무에서 자주 마주치는 패턴이다.
3.4.5 딕셔너리 컴프리헨션
3.4.6 set (집합)
중복을 허용하지 않고 순서가 없는 컬렉션이다. 수학의 집합 연산을 지원한다.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # {1, 2, 3, 4, 5, 6} 합집합
a & b # {3, 4} 교집합
a - b # {1, 2} 차집합
a ^ b # {1, 2, 5, 6} 대칭 차집합
# 중복 제거에 활용
items = [1, 2, 2, 3, 3, 3]
unique = list(set(items)) # [1, 2, 3]set은 내부적으로 해시 테이블을 사용하므로, in 연산이 \(O(1)\) 이다. 리스트에서 in 연산은 \(O(n)\) 이므로, 멤버십 검사가 빈번할 때 set이 훨씬 빠르다.
4 타입 변환
파이썬에서는 타입 간 변환이 자유롭다.
# 명시적 변환
int("42") # 42 str → int
float("3.14") # 3.14 str → float
str(42) # "42" int → str
list("abc") # ['a', 'b', 'c'] str → list
tuple([1, 2]) # (1, 2) list → tuple
set([1, 2, 2]) # {1, 2} list → set
# 실패하는 변환
# int("hello") # ValueError
# int("3.14") # ValueError — float("3.14") 후 int() 해야 한다type() 함수로 현재 타입을 확인하고, isinstance()로 타입을 검사한다.
5 None 타입
None은 “값이 없음”을 나타내는 특별한 객체이다. 다른 언어의 null/nil에 해당한다.
result = None
# None 비교는 is를 사용한다 (== 가 아니라)
if result is None:
print("결과가 없다")
# 함수가 명시적으로 return하지 않으면 None을 반환한다
def greet(name):
print(f"Hello, {name}")
x = greet("Alice") # "Hello, Alice" 출력
print(x) # Noneis와 ==의 차이: is는 같은 객체인지(identity), ==는 같은 값인지(equality)를 비교한다. None은 싱글턴 객체이므로 is None이 관례이다.
6 정리
| 개념 | 핵심 |
|---|---|
| 변수 | 값에 이름을 붙이는 것. 타입이 아니라 객체를 가리키는 이름표 |
| 동적 타이핑 | 타입 선언 없이 값을 할당. 같은 변수에 다른 타입 재할당 가능 |
| immutable | int, float, str, tuple, bool — 변경 시 새 객체 생성 |
| mutable | list, dict, set — 제자리에서 변경. 참조 공유 시 주의 |
| 타입 변환 | int(), float(), str(), list(), tuple(), set() |
| None | “값이 없음”. 비교 시 is None 사용 |
7 관련 주제
- 이전: 파이썬 입문: 컴퓨터 과학부터 개발환경 설정까지
- 다음: Python 제어 흐름문 (Control Flow Statements)
- 참고: 함수: 코드 재사용과 구조화의 핵심
- 참고: Pandas Copy — mutable 객체의 참조/복사 문제를 Pandas에서 다룬다