Python 변수와 데이터 타입

변수 선언, 동적 타이핑, 기본 자료형

파이썬의 변수 선언 방식, 동적 타이핑 특성, 기본 데이터 타입(int, float, str, bool, list, tuple, dict, set)을 다룬다.

Engineering
Python
저자

Kwangmin Kim

공개

2023년 07월 02일

1 개요

이전 글에서 파이썬의 특징과 개발환경 설정을 다루었다. 이 글에서는 프로그래밍의 가장 기본 단위인 변수데이터 타입을 다룬다.

프로그램은 결국 데이터를 입력받아 처리하고 결과를 출력하는 과정이다. 변수는 이 데이터를 저장하는 그릇이고, 데이터 타입은 그릇에 담을 수 있는 내용물의 종류를 결정한다. 파이썬은 이 그릇에 라벨만 붙이면 되고, 내용물의 종류는 파이썬이 알아서 판단한다. 이것이 동적 타이핑(dynamic typing)이다.

2 변수

2.1 변수란 무엇인가

변수(variable)는 값에 이름을 붙이는 것이다. 메모리에 저장된 데이터를 가리키는 이름표라고 이해하면 된다.

age = 25
name = "Alice"

age는 정수 25가 저장된 메모리 위치를 가리키는 이름이다. C나 Java에서는 int age = 25;처럼 타입을 먼저 선언해야 하지만, 파이썬에서는 age = 25만으로 충분하다. 파이썬이 25를 보고 “이것은 정수다”라고 자동으로 판단한다.

2.2 동적 타이핑

파이썬의 가장 큰 특징 중 하나이다. 같은 변수에 서로 다른 타입의 값을 재할당할 수 있다.

x = 10       # int
x = "hello"  # str로 변경
x = [1, 2]   # list로 변경

C/Java에서 int x = 10; 후에 x = "hello";를 시도하면 컴파일 에러가 발생한다. 파이썬은 변수가 타입을 갖는 것이 아니라, 값(객체)이 타입을 갖기 때문에 이것이 가능하다. 변수는 단지 객체를 가리키는 이름표일 뿐이다.

이 유연함은 프로토타이핑을 빠르게 하지만, 대규모 프로젝트에서는 타입 혼동으로 인한 버그 원인이 되기도 한다. 이를 보완하기 위해 파이썬 3.5부터 타입 힌트(type hint)를 지원한다.

# 타입 힌트: 실행에 영향을 주지 않지만 가독성과 도구 지원을 높인다
age: int = 25
name: str = "Alice"
scores: list[float] = [95.5, 87.3, 92.1]

2.3 변수 명명 규칙

규칙 예시 (O) 예시 (X)
영문자, 숫자, 언더스코어 사용 my_var, count2 my-var, 2count
숫자로 시작 불가 var1 1var
예약어 사용 불가 my_class class, for, if
대소문자 구분 Namename은 다른 변수

파이썬 커뮤니티는 snake_case(단어 사이에 언더스코어)를 관례로 사용한다. myVariable(camelCase)은 Java/JavaScript의 관례이다.

# 파이썬 관례 (PEP 8)
user_name = "Alice"       # 변수: snake_case
MAX_RETRY = 3             # 상수: UPPER_SNAKE_CASE
class UserProfile:        # 클래스: PascalCase
    pass

2.4 변수의 메모리 구조

파이썬에서 변수는 C처럼 “메모리 상자”가 아니라 “이름표(label)”이다. 이 차이는 중요하다.

a = [1, 2, 3]
b = a           # b는 a와 같은 리스트 객체를 가리킨다
b.append(4)
print(a)        # [1, 2, 3, 4] — a도 변경된다

b = a는 리스트를 복사하는 것이 아니라, 같은 객체에 이름표를 하나 더 붙이는 것이다. 이것을 이해하지 못하면 “내가 b만 바꿨는데 왜 a도 바뀌지?”라는 혼란에 빠진다. 독립적인 복사가 필요하면 b = a.copy() 또는 b = list(a)를 사용한다.

id() 함수로 객체의 메모리 주소를 확인할 수 있다.

a = [1, 2, 3]
b = a
print(id(a) == id(b))    # True — 같은 객체

c = a.copy()
print(id(a) == id(c))    # False — 다른 객체

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 = ab.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으로 넘어오면서 바뀐 동작이다.

진법 표현도 지원한다.

binary = 0b1010     # 2진수: 10
octal = 0o17        # 8진수: 15
hexadecimal = 0xFF  # 16진수: 255

3.1.2 float (부동소수점)

pi = 3.14159
scientific = 1.5e-3   # 0.0015

부동소수점은 IEEE 754 표준을 따르며, 정밀도 한계가 있다.

print(0.1 + 0.2)          # 0.30000000000000004
print(0.1 + 0.2 == 0.3)   # False

이것은 파이썬의 버그가 아니라 2진수로 소수를 표현할 때 발생하는 근본적인 한계이다. 금융 계산처럼 정확한 소수 연산이 필요하면 decimal 모듈을 사용한다.

from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))  # 0.3

실무에서 float 비교가 필요할 때는 math.isclose()를 사용한다.

import math
print(math.isclose(0.1 + 0.2, 0.3))  # True

3.2 문자열 (str)

문자열은 작은따옴표('...') 또는 큰따옴표("...")로 생성한다. 여러 줄 문자열은 삼중따옴표("""...""")를 사용한다.

single = 'hello'
double = "world"
multi = """여러 줄
문자열도
가능하다"""

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이다. 특정 문자를 바꾸려면 새 문자열을 생성해야 한다.

s = "Python"
# s[0] = 'J'  # TypeError 발생
s = 'J' + s[1:]  # "Jython" — 새 문자열 생성

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")         # 3

3.2.3 f-string (포맷 문자열)

파이썬 3.6부터 도입된 f-string은 가장 간결한 문자열 포맷팅 방법이다.

name = "Alice"
age = 25
score = 95.678

print(f"{name}{age}세이다")           # Alice은 25세이다
print(f"점수: {score:.2f}")              # 점수: 95.68
print(f"{'Python':>10}")                 # "    Python"  (오른쪽 정렬, 10칸)
print(f"{'Python':*^20}")                # "*******Python*******" (가운데 정렬)
print(f"{1000000:,}")                    # 1,000,000 (천단위 구분)

3.3 불리언 (bool)

TrueFalse 두 값만 가진다. 조건문과 논리 연산의 기반이다.

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

파이썬에서는 boolint의 하위 클래스이다. True1, False0으로 동작한다.

print(True + True)    # 2
print(True * 10)      # 10

3.3.1 Truthy와 Falsy

파이썬에서는 bool이 아닌 값도 조건문에서 True 또는 False로 평가된다. 이것을 truthy/falsy라고 한다.

Falsy (거짓으로 평가) Truthy (참으로 평가)
False True
0, 0.0, 0j 0이 아닌 모든 숫자
"" (빈 문자열) 내용이 있는 문자열
[], (), {}, set() (빈 컬렉션) 내용이 있는 컬렉션
None 그 외 모든 객체

이 규칙을 활용하면 코드를 간결하게 작성할 수 있다.

items = [1, 2, 3]

# 장황한 방식
if len(items) > 0:
    print("항목이 있다")

# 파이썬다운 방식
if items:
    print("항목이 있다")

빈 리스트는 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 리스트 컴프리헨션

파이썬의 강력한 기능 중 하나이다. 반복문과 조건문을 한 줄로 표현한다.

# 기본: 1~10의 제곱
squares = [x**2 for x in range(1, 11)]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 조건 포함: 짝수만
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 변환: 문자열 리스트를 대문자로
words = ["hello", "world"]
upper = [w.upper() for w in words]
# ["HELLO", "WORLD"]

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) 리스트보다 메모리를 적게 사용하고 접근 속도가 빠르다.

# 언패킹: 튜플의 가장 실용적인 활용
x, y = (3, 4)
print(x)    # 3
print(y)    # 4

# 함수에서 여러 값 반환
def min_max(numbers):
    return min(numbers), max(numbers)

lo, hi = min_max([3, 1, 4, 1, 5])
print(lo, hi)  # 1 5

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 딕셔너리 컴프리헨션

# 1~5의 제곱을 딕셔너리로
squares = {x: x**2 for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 리스트에서 빈도 계산
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
freq = {}
for w in words:
    freq[w] = freq.get(w, 0) + 1
# {"apple": 3, "banana": 2, "cherry": 1}

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이 훨씬 빠르다.

# 10만 건의 데이터에서 특정 값 검색
large_list = list(range(100_000))
large_set = set(range(100_000))

# large_list에서 99999 검색: O(n) — 느리다
# large_set에서 99999 검색: O(1) — 빠르다
99999 in large_set   # True (거의 즉시)

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()로 타입을 검사한다.

x = 42
print(type(x))              # <class 'int'>
print(isinstance(x, int))   # True
print(isinstance(x, (int, float)))  # True — 여러 타입 동시 검사

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)              # None

is==의 차이: 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 관련 주제

Subscribe

Enjoy this blog? Get notified of new posts by email: