1) 입출력 (Input / Output)
- sep 옵션 (기본값: sep = ' ' (공백))
print(2026, 1, 4, sep="/") # 2016/1/4
print("사과", "바나나", "포도", sep=", ") # 사과, 바나나, 포도
print("2", "0", "2", "6", sep="") # 2026
- end 옵션 (기본값: end = '\n' (줄바꿈))
for i in range(5):
print(".", end="")
print(" 완료!")
# ..... 완료!
- format()
item = "연필"
money = 1234567.89
print("저는 {}을(를) 좋아합니다. {}은(는) 유용합니다.".format(item, item))
print("{}은(는) {:,.1f}원 입니다.".format(item,money))
- f-string
- .format() 은 함수 호출 오버헤드가 발생하고 런타임에 문자열을 파싱하지만, f-string은 컴파일 시점에 상수를 분리하여 단순한 결합 연산으로 변환 (효율적)
name = "적토마"
print("{name}님, 환영합니다!") # f가 없으면 그대로 출력됩니다.
print(f"{name}님, 환영합니다!") # f가 있으면 값으로 바뀝니다.
exp = f"{name}님, 환영합니다!" # f-string으로 직접 변수 생성
print(exp)
money = 9876543
print(f"총액: {money:,}원")
avg = 3.14159
print(f"평균: {avg:.2f}")
- input()
- 결과는 무조건 문자열(str)임
- 문자열을 숫자로 바꾸기 : int(), float()
- hello, 3.14를 int로 바꾸려고 하거나
- 1,000 등 int나 float가 아닌 값을 함수에 넣으면 에러 발생!
- → 프로그램이 멈추지 않게 하려면 try/except 사용!
- 입력 개수가 다르면 에러 발생
- a, b = ... 는 딱 2개가 들어와야 함
s = input("정수 2개를 공백으로 입력하세요(예: 10 0): ")
try:
a, b = map(int, s.split())
print("a / b =", a / b)
except ValueError:
print("입력 형식이 잘못되었습니다! 예: 10 3") # 정수가 아닌 경우 포함
except ZeroDivisionError:
print("0으로는 나눌 수 없습니다!")
- I/O와 버퍼링 (Buffering) : 왜 input()은 느린가?
- input()
- 사용자가 한 줄을 입력할 때마다 즉각적으로 반응하고,
- 내부적으로 문자열 변환과 strip() 처리를 수행하는 등 오버헤드가 큼
- sys.stdin.readline()
- 버퍼링(Buffering)을 적극 활용함
- 운영체제가 입력 데이터를 큰 바구니(버퍼)에 한꺼번에 담아 메모리에 올려두면,
- 프로그램은 메모리에서 데이터를 읽어오기만 하면 됨
- 시스템 콜(System Call)의 횟수를 줄이는 것! → 성능 최적화
- input()
- split()
- 공백을 기준으로 리스트를 만듦
- 구분자가 달라지면 split도 달라져야 함
- 공백 입력: split()
- 쉼표 입력: split(',')
- # 입력: split('#')
- 결과에 공백이 남을 수 있음
- → 습관적으로 strip() 하는 것이 안전함
print("Read#Analyze#Implement".split("#"))
# ['Read', 'Analyze', 'Implement']
# 연속 공백은 split()가 알아서 정리해 줍니다.
print("10 20 30".split())
# ['10', '20', '30']
# 하지만 split(',')은 공백을 자동으로 없애주지 않습니다.
print("10, 20, 30".split(","))
# ['10', ' 20', ' 30']
# 공백이 거슬리면 strip()으로 정리할 수 있습니다.
parts = "10, 20, 30".split(",")
clean = [p.strip() for p in parts]
print(clean)
- map(int, ...)
- map 함수가 반환하는 것은 map object인데, 이는 리스트는 아니지만 '한번에 하나씩 값을 꺼내줄 수 있는 봉투'와 같음
- sum() 함수는 이 봉투 안에서 값이 다 나올 때까지 하나씩 꺼내서 더하기 때문에, 굳이 리스트라는 실제 바구니에 담아두지 않아도 합계를 구할 수 있는 것
- map(function, iterable)
- 첫번째 인자는 Collable(함수 객체)
- 두번째 인자는 Iterable
# 쉼표 뒤 공백이 있는 경우: 먼저 strip 처리하기
parts = "10, 20, 30".split(",")
nums = list(map(int, [p.strip() for p in parts]))
print(nums)
# [10, 20, 30]
# 사용자 입력 예시(직접 입력): 10#20#30
scores = list(map(int, input("정수 3개(#): ").split("#")))
print("최댓값:", max(scores), "최솟값:", min(scores))
# 정수 3개(#): 10#50#60
# 최댓값: 60 최솟값: 10
data = input()
data2 = map(int,data.replace(',','').split())
print(sum(data))
# 리스트 안에서 직접 정수 변환 반복
data3 = [int(i) for i in data.replace(',','').split()]
print(sum(data))
# map 객체 안의 결과물을 실제 리스트로 변환
data4 = list(map(int, data.replace(',','').split()))
print(sum(data)) # 이제 [100, 200] 형태가 되어 잘 작동합니다.
- 미니 프로젝트: "안전한 계산기" 만들기
- 경우를 나누어주기 위해서 replace를 두번 연달아 사용하고
- 공백을 기준으로 요소를 나누어주기 위해서 split 바로 사용
# s = input("두 수를 입력하세요 (예: 10 3 / 10,3 / 10#3): ")
# TODO: 구분자 선택
# TODO: 숫자 변환(필요하면 strip, replace 등)
cleaned = s.replace(',',' ').replace('#',' ').split()
print(cleaned)
# TODO: 합/차/곱/나눗셈 출력
try:
num = list(map(int, [p.strip() for p in cleaned]))
print("addition = ", num[0]+num[1])
print("subtraction = ", num[0]-num[1])
print("multiplication = ", num[0]*num[1])
print("division = ", num[0]/num[1])
# TODO: ValueError, ZeroDivisionError 처리
except ValueError:
print("입력 형식이 잘못되었습니다.")
except ZeroDivisionError:
print("0으로는 나눌 수 없습니다!")
2) 변수 (Variables)
- type(변수명)
- 다중할당 (Multiple Assignment)
# 리스트 같은 것도 나눠 담을 수 있습니다(맛보기)
p, q = [7, 9]
print(p, q) # 7 9
# input과 같이 쓰는 패턴(직접 입력: 예 3 7)
a, b = map(int, input("정수 2개(공백): ").split())
print("합:", a + b)
# (헷갈림) 변수 개수와 값 개수가 다르면 에러
# a, b = 1, 2, 3 # ValueError
print("개수 불일치 예시는 주석으로 막아두었습니다.")
- 전역/지역 변수
global_value = 10
def show_value():
print("함수 안(읽기):", global_value)
show_value()
print("함수 밖:", global_value)
def add_one_wrong():
# 아래 주석을 풀면 UnboundLocalError가 날 수 있습니다.
# global_value = global_value + 1
pass
print("수정 오류 예시는 주석으로 막아두었습니다.")
- 해결방법
- return으로 새 값을 돌려받기
- global 키워드 사용하기
# return으로 새 값을 돌려받기 예제
count = 0
def inc(value):
return value + 1
count = inc(count)
count = inc(count)
print(count)
# global 키워드 사용하기 예제
total = 0
def add_to_total(x):
global total
total = total + x
add_to_total(5)
print(total)
add_to_total(7)
print(total)
# 지역 변수는 함수 밖에서 못 씀
def make_local():
local_value = 123
print("함수 안:", local_value)
make_local()
# print(local_value) # 주석 해제 시 NameError
- 동적 타이핑과 런타임 오버헤드
- 파이썬에서 변수의 타입은 실행시점(Runtime)에 결정됨
- 동작방식
- C언어는 컴파일 타임에 int a 라고 선언하면 메모리에 딱 4바이트를 비워둠
- 하지만 파이썬은 변수에 값을 넣을 떄마다 인터프리터가 객체의 타입을 확인하고 (Type Checking)
- 그에 맞는 메모리를 힙(Heap)에 할당하며, 참조 횟수(Reference Count)를 관리함
- 비용
- 이 모든 과정이 프로그램 실행 중에 일어나기 때문에 정적 언어보다 속도가 느림
- 파이썬은 편리하지만 실행 성능(Runtime Performance)을 희생한 언어!
- 미니 프로젝트: "용돈 계산기" (변수 총정리)
# TODO: 값은 자유롭게 바꿔도 됩니다.
allowance = 50000
snack = 12000
transport = 15000
game = 8000
# TODO 1: 총 지출 계산
total_spent = snack + transport + game
# TODO 2: 남은 돈 계산
remaining = allowance - total_spent
# TODO 3: 출력 + 조건 처리
print(f"남은 돈은 {int(remaining)}원 입니다.")
3) 문자열 (Strings)
- 불변 (immutable) : 한번 만든 문자열은 그 자리에서 수정할 수 없음
- replace("h", "H", 1) # 첫 번째 h만 H로 바꿈
# 예제 1: replace로 바꾸기
word = "hello"
new_word = word.replace("h", "H", 1) # 첫 번째 h만 바꿈
print(word, "->", new_word)
# 예제 2: 슬라이싱으로 조합하기
word = "hello"
new_word = "H" + word[1:]
print(word, "->", new_word)
- step(간격)까지 포함한 슬라이싱
- step=2 : 한 글자씩 건너뜀
- step=-1 : 뒤집음
- end는 포함x (s[0:2]는 0과 1까지만! 2는 포함x)
- 인덱스가 범위를 넘어가면?
- 인덱스 s[999]는 에러 (범위 밖)
- 슬라이싱 s[0:999]는 에러가 아니라 가능한 만큼만 가져옴
x = "abcdefghijklmnop"
print(x[0:-1:2]) # 두 칸씩 띄워서 0번째부터 -1번째(마지막)까지
s = "abcdefg"
print("[::-1] 뒤집기:", s[::-1]) # 처음부터 끝까지 출력할건데, 거꾸로 출력해라
s = "abcdefg"
print("[-5:] 뒤에서 5글자:", s[-5:]) # cdefg
s = "0123456789"
print("[::2] 짝수 인덱스:", s[::2])
s = "Korea"
print("[0:100] end가 넘어가도 괜찮음:", s[0:100])
- 시작과 끝 인덱스가 같으면 범위 없음
s = "Python"
print(f"'{s[0:0]}'") # 빈 문자열 '' 출력
# 설명: 시작과 끝 인덱스가 같으면 빈 문자열이 반환됩니다.
- 인덱싱과 슬라이싱의 차이
s = "Python"
print(s[-1],type(s[-1]))
print(s[-1:],type(s[-1:]))
# 결과는 같아보이지만 사실 차이가 있음
# 인덱싱을 한 것은 마지막 문자 하나를 불러오지만
# 슬라이싱을 한 것은 마지막 문자 하나를 포함한 부분 문자열을 불러옴
list = [1,2,3,4,5]
print(list[-1],type(list[-1])) # 5 int
print(list[-1:],type(list[-1:])) # [5] list
- strip(), split(","), replace(), join()
text = "\t\t\n\n hello \n\n\t\t"
print("원본:", repr(text))
print("strip:", repr(text.strip()))
print("lstrip:", repr(text.lstrip()))
print("rstrip:", repr(text.rstrip()))
# (자주 쓰는 패턴) split 전에 strip
line = " 10, 20, 30 "
parts = [p.strip() for p in line.strip().split(",")]
print(parts)
# 여러 번 등장하면 모두 바뀝니다.
s = "ha ha ha"
print(s.replace("ha", "ho")) # hohoho
# 첫 번째만 바꾸고 싶으면 count 인자 사용(선택)
s = "banana"
print(s.replace("a", "A", 1)) #bAnana
# 쉼표 뒤 공백이 있으면?
s = "10, 20, 30"
parts = s.split(",")
print(parts)
clean = [p.strip() for p in parts]
print(clean)
# join: 리스트를 문자열로 합치기
words = ["I", "like", "python"]
print(" ".join(words))
# join: 구분자를 바꿀 수도 있습니다.
words = ["2026", "01", "04"]
print("/".join(words))
# (헷갈림) split은 리스트, join은 문자열
s = "a,b,c"
parts = s.split(",")
print(parts, type(parts))
# ['a', 'b', 'c'] <class 'list'>
print("-".join(parts), type("-".join(parts)))
# a-b-c <class 'str'>
# join은 리스트 요소가 "문자열"이어야 합니다.
# 아래는 에러 예시입니다.
# print(",".join([1, 2, 3]))
# 해결: 숫자는 str로 바꾼 뒤 join
nums = [1, 2, 3]
print(",".join(map(str, nums)))
- count(), find()
# count와 find를 같이 사용하면 전처리에 도움이 됩니다.
s = "a,b,c,d"
print("쉼표 개수:", s.count(","))
print("첫 쉼표 위치:", s.find(","))
# 두 번째 위치를 찾고 싶다면: 첫 번째 위치 다음부터 다시 find
s = "ababa"
first = s.find("ba")
second = s.find("ba", first + 1)
print(first, second)
- find() vs. index() (중요)
- 둘다 '부분 문자열의 위치'를 찾는 기능이지만,
- find(): 없으면 -1을 반환 (안전하게 분기 가능)
- index(): 없으면 에러(ValueError) 발생
- 둘다 '부분 문자열의 위치'를 찾는 기능이지만,
# index를 안전하게 쓰려면 try/except를 씁니다.
s = "Hello Python"
try:
pos = s.index("Java")
print("위치:", pos)
except ValueError:
print("index: 찾지 못했습니다!")
# 실전 예시: 이메일에서 @ 위치 찾기
email = "abc@gmail.com"
at = email.find("@")
print("at 위치:", at)
print("아이디:", email[:at])
print("도메인:", email[at+1:])
# if pos: 같은 식으로 쓰면 안 됩니다(0이 False처럼 취급될 수 있음)
s = "abc"
pos = s.find("a") # 0
if pos:
print("찾음(잘못된 판단 가능)")
else:
print("pos가 0이라서 else로 갑니다(실제로는 찾았는데!)")
# 올바른 판단:
if pos != -1:
print("올바르게: 찾았습니다!")
- raw 문자열: r'...' (경로/정규표현식에서 특히 중요
- 이스케이프 문자 (escape)
- \n: 줄바꿈
- \t: 탭
- \\: 역슬래시 자체를 의미
- raw 문자열 r'...'을 쓰면:
- 역슬래시(\)를 특별하게 해석하지 않고 '그대로' 취급함
- 이스케이프 문자 (escape)
# 일반 문자열: \n이 줄바꿈으로 해석될 수 있습니다.
path = "C:\new\test"
print(path)
# Raw 문자열: 역슬래시를 그대로 취급합니다.
path = r"C:\new\test"
print(path)
- repr()
- Representation(표현)의 약자, 객체를 인간이 이해할 수 있는 문자열로 반환
- 이 반환된 문자열을 그대로 파이썬 코드 창에 붙여넣었을 때, 다시 그 객체를 만들 수 있을 정도의 정보를 담음
import datetime
now = datetime.datetime.now()
print(str(now)) # 출력: 2024-05-22 14:30:01.123456 (읽기 편함)
print(repr(now)) # 출력: datetime.datetime(2024, 5, 22, 14, 30, 1, 123456) (객체 생성 정보 포함)
a = "7"
b = 7
# print로 보면 둘 다 7처럼 보임
print(a) # 7
print(b) # 7
# repr로 보면 차이가 명확함
print(repr(a)) # '7' (따옴표가 붙어 문자열임을 명시)
print(repr(b)) # 7 (숫자임을 알 수 있음)
- 미니 프로젝트: "문장 정리기" 만들기
text = input("문장을 입력하세요: ")
# TODO 1: 공백 제거
cleaned = text.strip()
# TODO 2: 소문자 변환
lower = cleaned.lower()
# TODO 3: 쉼표를 공백으로 바꾸기
blank = lower.replace(',',' ')
# TODO 4: 단어 리스트 만들기(split) + 단어 개수 출력
words = blank.split()
print(len(words))
# TODO 5: python 포함 여부 출력
w = 'python'
try:
pos = words.index(w)
print("위치: ",pos)
except ValueError:
print("index: 찾지 못했습니다!")
# GEMINI 추천: 특정 단어 포함되어있는지 확인
if w in words:
print(f"{w}가 리스트에 포함되어있습니다.")
# GEMINI 추천: 메서드 체이닝 (Method Chaining)
text = input("문장을 입력하세요: ").strip().lower().replace(',', ' ')
words = text.split()
print(f"단어 개수: {len(words)}")
print(f"python 포함 여부: {'python' in words}")
'Python 공부' 카테고리의 다른 글
| 클래스, 생성자, 캡슐화, 상속, 오버라이딩, 매직 메서드 (0) | 2026.01.14 |
|---|---|
| 리스트, 튜플, 딕셔너리 (1) | 2026.01.10 |
| 정적 할당과 동적 할당 / 파이썬 int 자료형 범위 (0) | 2026.01.09 |
| 타입 힌트, is 연산자 vs == 연산자 (0) | 2026.01.09 |
| SQL + Python 연동 (0) | 2026.01.09 |