변수와 이름 규칙
변수는 데이터에 붙이는 이름입니다. 파이썬 변수명은 다음과 같은 엄격한 규칙을 따르며, 위반 시 오류가 발생합니다.
| 규칙 | 올바른 예시 | 잘못된 예시 |
| 영문자, 숫자, 밑줄만 사용 | user_name |
user-name |
| 숫자로 시작 불가 | data_1 |
1_data |
| 공백 포함 불가 | my_score |
my score |
| 예약어 사용 불가 | total |
class, print |
규칙 외에도 관례적으로 지키는 습관이 있습니다.
- 변수명은 소문자로 시작하고, 여러 단어는 밑줄로 연결합니다 (예:
student_age). - 의미를 알 수 있도록 짓고, 임시 변수가 아니라면
a,b,x같은 의미 없는 이름은 피합니다. - 클래스명은 대문자로 시작하는 카멜 표기법을 사용합니다 (예:
StudentInfo).
# 좋은 이름
student_name = "철수"
total_score = 95
is_logged_in = True
# 나쁜 이름 (오류는 없지만 가독성이 떨어짐)
a = "철수"
ts = 95
flag = True
기본 데이터 타입
파이썬에서 가장 많이 사용되는 데이터 타입입니다.
| 타입 | 설명 | 예시 |
int |
정수 | age = 18 |
float |
실수 (소수점) | price = 9.9 |
str |
문자열 | name = "Alice" |
bool |
불리언 (참/거짓) | is_ok = True |
NoneType |
값 없음 | result = None |
type() 함수로 변수의 타입을 확인하고, int(), str(), float() 등을 사용하여 타입을 변환할 수 있습니다.
x = "123"
print(type(x)) # <class 'str'>
x = int(x)
print(type(x)) # <class 'int'>
print(x + 1) # 124
참고: True와 False는 숫자 연산에서 각각 1과 0처럼 동작합니다. 예를 들어 True + 1의 결과는 2입니다.
문자열
문자열은 따옴표로 감싼 텍스트입니다. 작은따옴표와 큰따옴표는 기능적으로 동일합니다.
생성과 결합
s1 = "Hello"
s2 = 'World'
# + 연산자로 결합
s3 = s1 + " " + s2 # "Hello World"
# * 연산자로 반복
s4 = "ha" * 3 # "hahaha"
# f-string (Python 3.6+ 권장)
name = "철수"
age = 18
s5 = f"제 이름은 {name}이고, 나이는 {age}살입니다." # "제 이름은 철수이고, 나이는 18살입니다."
자주 사용하는 메서드
text = " Hello, Python! "
# 공백 제거
text.strip() # "Hello, Python!" 양쪽 공백 제거
text.lstrip() # "Hello, Python! " 왼쪽 공백 제거
text.rstrip() # " Hello, Python!" 오른쪽 공백 제거
# 대소문자 변환
"hello".upper() # "HELLO"
"HELLO".lower() # "hello"
"hello world".title() # "Hello World"
# 문자열 치환
"Hello World".replace("World", "Python") # "Hello Python"
# 분할
"a,b,c".split(",") # ['a', 'b', 'c']
# 리스트를 문자열로 결합
",".join(["a", "b", "c"]) # "a,b,c"
# 검색
"Hello".find("ll") # 2 (시작 인덱스 반환)
"Hello".count("l") # 2 (등장 횟수)
인덱싱과 슬라이싱
문자열의 각 문자는 0부터 시작하는 인덱스를 가지며, 음수 인덱스는 문자열 끝에서부터 셉니다.
문자열: H e l l o
정방향: 0 1 2 3 4
역방향: -5 -4 -3 -2 -1
s = "Hello"
s[0] # "H" 첫 번째 문자
s[-1] # "o" 마지막 문자
s[1:4] # "ell" 인덱스 1, 2, 3 (인덱스 4는 포함 안 함)
s[:3] # "Hel" 처음부터 인덱스 3까지
s[2:] # "llo" 인덱스 2부터 끝까지
s[::-1] # "olleH" 문자열 뒤집기
참고: 슬라이싱은 왼쪽은 포함, 오른쪽은 미포함입니다. s[1:4]는 인덱스 1, 2, 3을 가져옵니다.
길이 구하기
len("Hello") # 5
len("안녕") # 2 (한글도 한 글자로 셈)
리스트
리스트는 대괄호 []로 표현하며, 여러 타입의 데이터를 순서대로 저장합니다.
생성
# 빈 리스트
fruits = []
# 직접 생성
fruits = ["사과", "바나나", "오렌지"]
# 여러 타입 혼합
mixed = [1, "hello", True, 3.14]
# range()로 숫자 리스트 생성
numbers = list(range(1, 6)) # [1, 2, 3, 4, 5]
numbers = list(range(0, 10, 2)) # [0, 2, 4, 6, 8] 간격 2
# 리스트 컴프리헨션 (간결한 표현)
squares = [x**2 for x in range(1, 6)] # [1, 4, 9, 16, 25]
요소 추가
fruits = ["사과", "바나나"]
fruits.append("오렌지") # 끝에 추가 → ["사과", "바나나", "오렌지"]
fruits.insert(1, "포도") # 지정 위치에 추가 → ["사과", "포도", "바나나", "오렌지"]
요소 삭제
fruits = ["사과", "바나나", "오렌지", "포도"]
del fruits[0] # 인덱스로 삭제
fruits.pop() # 마지막 요소 삭제 후 반환
fruits.pop(1) # 인덱스 1의 요소 삭제 후 반환
fruits.remove("바나나") # 값으로 삭제 (첫 번째 일치하는 항목만)
조회와 접근
fruits = ["사과", "바나나", "오렌지"]
fruits[0] # "사과"
fruits[-1] # "오렌지"
fruits[0:2] # ["사과", "바나나"] 슬라이싱은 문자열과 동일
# 순회
for fruit in fruits:
print(fruit)
# 인덱스를 함께 순회
for i, fruit in enumerate(fruits):
print(i, fruit)
# 존재 여부 확인
"바나나" in fruits # True
"수박" not in fruits # True
요소 변경
fruits = ["사과", "바나나", "오렌지"]
fruits[1] = "수박" # ["사과", "수박", "오렌지"]
기타 유용한 연산
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
len(numbers) # 8 길이
max(numbers) # 9 최대값
min(numbers) # 1 최소값
sum(numbers) # 31 합계
numbers.sort() # 원본 리스트 정렬 (영구적 변경)
sorted(numbers) # 정렬된 새 리스트 반환 (원본 유지)
numbers.reverse() # 원본 리스트 뒤집기
# 리스트 복사 (슬라이싱 활용, 참조 공유 방지)
copy = numbers[:]
튜플
튜플은 소괄호 ()로 표현하며, 리스트와 유사하지만 생성 후 변경이 불가능합니다.
# 생성
point = (3, 4)
colors = ("빨강", "초록", "파랑")
single = (1,) # 요소가 하나일 때는 반드시 쉼표를 붙여야 함
# 접근 (리스트와 동일)
point[0] # 3
point[-1] # 4
point[0:2] # (3, 4)
# 순회
for color in colors:
print(color)
# 변경 시도 시 오류 발생
# point[0] = 10 ← TypeError
튜플을 사용하는 경우
- 데이터가 변경되면 안 될 때 (좌표, RGB 색상 값, 함수의 여러 반환 값)
- 딕셔너리의 키로 사용할 때 (리스트는 키로 사용 불가)
- 고정된 구조의 데이터를 표현할 때
딕셔너리
딕셔너리는 중괄호 {}로 표현하며, 키-값 쌍을 저장합니다. 위치가 아닌 키로 값을 찾습니다.
생성
# 직접 생성
student = {
"name": "철수",
"age": 18,
"score": 95
}
# 빈 딕셔너리
info = {}
추가와 수정
student = {"name": "철수", "age": 18}
# 키가 없으면 추가
student["score"] = 95
# 키가 있으면 수정
student["age"] = 19
# 여러 항목 한 번에 업데이트
student.update({"age": 20, "city": "서울"})
삭제
student = {"name": "철수", "age": 18, "score": 95}
del student["score"] # 직접 삭제
age = student.pop("age") # 삭제 후 값 반환
age = student.pop("age", 0) # 키가 없으면 기본값 0 반환 (오류 방지)
last = student.popitem() # 마지막에 추가된 항목 삭제 후 반환
조회
student = {"name": "철수", "age": 18}
student["name"] # "철수" (키가 없으면 오류 발생)
student.get("name") # "철수" (키가 없으면 None 반환)
student.get("score", 0) # 키가 없으면 기본값 0 반환
"name" in student # True 키 존재 여부 확인
순회
student = {"name": "철수", "age": 18, "score": 95}
# 키 순회
for key in student:
print(key)
# 값 순회
for value in student.values():
print(value)
# 키와 값 동시 순회 (가장 자주 사용)
for key, value in student.items():
print(f"{key}: {value}")
조건문
기본 구조
score = 85
if score >= 90:
print("훌륭함")
elif score >= 70:
print("좋음")
elif score >= 60:
print("통과")
else:
print("재시험")
비교 연산자
| 연산자 | 의미 | 예시 |
== |
같다 | a == b |
!= |
다르다 | a != b |
>, <, >=, <= |
크기 비교 | a > b |
and |
그리고 (모두 참) | a > 0 and b > 0 |
or |
또는 (하나라도 참) | a > 0 or b > 0 |
not |
부정 | not a > 0 |
in |
포함 여부 | "a" in lst |
not in |
미포함 여부 | "a" not in lst |
age = 20
city = "서울"
if age >= 18 and city == "서울":
print("조건 충족")
# 리스트 내 요소 확인
allowed = ["사과", "바나나", "오렌지"]
if "사과" in allowed:
print("먹을 수 있음")
반복문
for 반복문
# 리스트 순회
fruits = ["사과", "바나나", "오렌지"]
for fruit in fruits:
print(fruit)
# 숫자 범위 순회
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8
print(i)
# 딕셔너리 순회
info = {"name": "철수", "age": 18}
for key, value in info.items():
print(key, value)
while 반복문
count = 0
while count < 5:
print(count)
count += 1
break와 continue
# break: 반복문 즉시 종료
for i in range(10):
if i == 5:
break # 5를 만나면 멈춤
print(i) # 0 1 2 3 4 출력
# continue: 현재 반복 건너뛰고 다음 반복으로
for i in range(10):
if i % 2 == 0:
continue # 짝수는 건너뜀
print(i) # 1 3 5 7 9 출력
함수
함수는 재사용 가능한 코드 블록을 정의하는 방법입니다.
정의와 호출
# 정의
def greet(name):
print(f"안녕하세요, {name}님!")
# 호출
greet("철수") # 안녕하세요, 철수님!
greet("영희") # 안녕하세요, 영희님!
반환 값
def add(a, b):
return a + b
result = add(3, 5) # result = 8
# 여러 값 반환 (실제로는 튜플 반환)
def min_max(lst):
return min(lst), max(lst)
low, high = min_max([3, 1, 4, 1, 5, 9])
# low = 1, high = 9
매개변수 종류
# 기본값 매개변수: 호출 시 생략 가능
def greet(name, msg="안녕하세요"):
print(f"{msg}, {name}님!")
greet("철수") # 안녕하세요, 철수님!
greet("영희", "좋은 아침") # 좋은 아침, 영희님!
# 키워드 인자: 순서 상관없이 이름으로 전달
def describe(name, age, city):
print(f"{name}, {age}세, {city} 출신")
describe(age=18, city="서울", name="철수")
# *args: 여러 개의 위치 인자를 튜플로 받음
def total(*args):
return sum(args)
total(1, 2, 3) # 6
total(1, 2, 3, 4, 5) # 15
# **kwargs: 여러 개의 키워드 인자를 딕셔너리로 받음
def show_info(**kwargs):
for k, v in kwargs.items():
print(f"{k}: {v}")
show_info(name="철수", age=18, city="서울")
매개변수 순서 규칙
함수 정의 시 매개변수 순서는 다음과 같아야 합니다.
일반 매개변수 → 기본값 매개변수 → *args → **kwargs
def func(a, b, c=10, *args, **kwargs):
pass
클래스와 객체
클래스는 객체를 생성하기 위한 템플릿이며, 객체는 클래스의 구체적인 인스턴스입니다.
클래스 정의
class Dog:
# 클래스 속성: 모든 개가 공유하는 속성
species = "개과"
# 초기화 메서드: 객체 생성 시 자동 호출
def __init__(self, name, age):
# 인스턴스 속성: 각 개체가 가지는 고유 속성
self.name = name
self.age = age
# 메서드: 개가 할 수 있는 동작
def bark(self):
print(f"{self.name}가(이) 왈왈! 하고 짖습니다.")
def info(self):
print(f"이름: {self.name}, 나이: {self.age}세")
객체 생성과 활용
# 객체 생성 (인스턴스화)
dog1 = Dog("멍멍이", 3)
dog2 = Dog("하얀", 1)
# 메서드 호출
dog1.bark() # 멍멍이가(이) 왈왈! 하고 짖습니다.
dog2.info() # 이름: 하얀, 나이: 1세
# 속성 접근
print(dog1.name) # 멍멍이
print(Dog.species) # 개과 (클래스 속성은 클래스 이름으로 접근)
print(dog1.species) # 개과 (인스턴스를 통해서도 접근 가능)
# 속성 수정
dog1.age = 4 # 직접 수정
상속
자식 클래스는 부모 클래스의 모든 메서드와 속성을 상속받고, 새로운 기능을 추가하거나 기존 기능을 재정의할 수 있습니다.
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}가(이) 먹이를 먹습니다.")
class Cat(Animal): # Cat은 Animal을 상속
def meow(self):
print(f"{self.name}가(이) 야옹~ 하고 웁니다.")
# 부모 메서드 재정의 (오버라이딩)
def eat(self):
print(f"{self.name}가(이) 우아하게 먹이를 먹습니다.")
cat = Cat("나비")
cat.eat() # 나비가(이) 우아하게 먹이를 먹습니다. (자식 메서드 호출)
cat.meow() # 나비가(이) 야옹~ 하고 웁니다.
super()를 사용하여 부모 클래스의 메서드를 호출할 수 있습니다.
class GuideDog(Dog):
def __init__(self, name, age, owner):
super().__init__(name, age) # 부모 Dog의 __init__ 호출
self.owner = owner # 새로운 속성 추가
def guide(self):
print(f"{self.name}가(이) 주인 {self.owner}를(을) 안내합니다.")
메서드 종류
| 메서드 유형 | 작성법 | 설명 |
| 인스턴스 메서드 | def method(self) |
인스턴스 속성에 접근, 인스턴스 이름으로 호출 |
| 클래스 메서드 | @classmethod + def method(cls) |
클래스 속성에 접근, 클래스 이름으로 호출 |
| 정적 메서드 | @staticmethod + def method() |
인스턴스나 클래스와 무관, 클래스 내 일반 함수 |
class MathHelper:
pi = 3.14159
@classmethod
def get_pi(cls):
return cls.pi
@staticmethod
def add(a, b):
return a + b
MathHelper.get_pi() # 3.14159
MathHelper.add(3, 5) # 8
자주 사용하는 내장 함수
| 함수 | 설명 | 예시 |
print() |
출력 | print("hello") |
input() |
사용자 입력 받기 | name = input("이름 입력: ") |
len() |
길이 반환 | len([1, 2, 3]) → 3 |
type() |
타입 확인 | type(123) → <class 'int'> |
int(), float(), str() |
타입 변환 | int("123") → 123 |
range() |
숫자 시퀀스 생성 | range(1, 6) |
list(), tuple(), dict(), set() |
컬렉션 타입 변환 | list(range(5)) |
max(), min(), sum() |
최대, 최소, 합계 | max([3,1,4]) → 4 |
abs() |
절대값 | abs(-5) → 5 |
round() |
반올림 | round(3.567, 2) → 3.57 |
sorted() |
정렬된 새 리스트 반환 | sorted([3,1,2]) → [1,2,3] |
enumerate() |
인덱스와 함께 순회 | for i, v in enumerate(lst) |
zip() |
여러 리스트 동시 순회 | for a, b in zip(lst1, lst2) |
헷갈리기 쉬운 개념
할당은 복사가 아님
a = [1, 2, 3]
b = a # b와 a는 같은 리스트를 가리킴 (복사 아님!)
b.append(4)
print(a) # [1, 2, 3, 4] a도 변경됨
# 올바른 복사 방법
b = a[:] # 또는 b = list(a)
=와 ==의 차이
x = 5 # 할당: 5를 x에 저장
x == 5 # 비교: x가 5와 같은지 확인, True 또는 False 반환
정수 나눗셈
10 / 3 # 3.3333... 일반 나눗셈, 결과는 실수
10 // 3 # 3 정수 나눗셈, 몫만 반환
10 % 3 # 1 나머지 연산
리스트 vs 튜플
- 데이터가 변경될 가능성이 있으면 리스트 사용
- 데이터가 고정되어 있으면 (좌표, 설정 등) 튜플 사용
- 딕셔너리의 키로 사용해야 하면 튜플만 가능 (리스트는 불가)
딕셔너리 []와 .get()의 차이
d = {"name": "철수"}
d["age"] # KeyError! 키가 없으면 오류 발생
d.get("age") # None, 키가 없으면 None 반환 (오류 없음)
d.get("age", 0) # 0, 키가 없으면 지정한 기본값 반환