객체지향 프로그래밍(Object-Oriented Programming, OOP)
객체지향 프로그래밍은 프로그램을 객체(Object)들의 모임으로 구성하는 프로그래밍 패러다임이다.
특징
- 객체(Object)
- 데이터(속성, attributes)와 이를 처리하는 메서드(methods)를 하나로 묶은 단위
- 클래스와 객체의 차이
- 클래스: 객체를 만들기 위한 설계도 e.g. 사람(Person)
- 객체: 클래스를 기반으로 만들어진 실체 e.g. 영희(Person 객체)
- 클래스와 객체의 차이
- 데이터(속성, attributes)와 이를 처리하는 메서드(methods)를 하나로 묶은 단위
- 주요 개념
- 캡슐화(Encapsulation): 데이터와 메서드를 객체 내부에 감추고 접근을 제한
- 상속(Inheritance): 기존 클래스(부모)에서 새로운 클래스(자식)를 생성하여 코드 재사용 가능
- 다형성(Polymorphism): 같은 메서드명을 유지하면서 서로 다른 동작을 수행할 수 있음
- 장점
- 코드의 재사용성 증가
- 유지보수성 향상
- 실제 세계 모델링 용이
예제 코드
class Dog:
def __init__(self, name): # 속성 정의
self.name = name
def bark(self): # 메서드 정의
return f"{self.name}가 멍멍!"
PEP 8 스타일 가이드
공백 사용
- 탭 대신 4칸 스페이스를 사용해 들여쓰기
- 클래스와 함수 사이에는 빈 줄 두 줄, 클래스 내 메서드 사이에는 한 줄
- 딕셔너리에서
키:값
사이에는 공백 없이,키: 값
형태로 사용 - 변수 대입에서
=
전후에 공백 한 칸 사용 - 타입 표기 시
변수명:타입
(공백 없음),: 타입
(공백 있음)
명명 규칙
- 함수, 변수:
lowercase_underscore
(snake_case) - 보호된 속성:
_leading_underscore
- 비공개 속성:
__double_leading_underscore
- 클래스 및 예외:
CapitalizedWord
(PascalCase) - 모듈 수준 상수:
ALL_CAPS
- 인스턴스 메서드 첫 번째 인자:
self
- 클래스 메서드 첫 번째 인자:
cls
식과 문 작성 규칙
- 부정적인 식을 만들지 말고
if a is not b
형태로 표현 - 빈 컨테이너 확인 시
if not container:
활용 if container:
방식으로 비어 있지 않은지 확인\
대신 괄호를 사용해 여러 줄에 걸쳐 식을 작성
임포트 규칙
- 표준 라이브러리 → 서드파티 라이브러리 → 사용자 정의 모듈 순서로 정렬
- 모듈을 임포트할 때 절대 경로 사용 (
from bar import foo
) - 상대 경로가 필요하면
from . import foo
처럼 명시적으로 작성
1-3 bytes와 str 차이
사용 사례 | str (문자열) |
bytes (바이트) |
---|---|---|
일반 텍스트 처리 | 사용 | 불필요 |
파일 입출력 | 텍스트 파일 (.txt , .csv ) |
바이너리 파일 (.jpg , .mp4 ) |
네트워크 통신 | 불가능 | 필수 (socket 통신 등) |
암호화 & 해싱 | 불가능 | 필수 (hashlib , 암호화 알고리즘) |
데이터 직렬화 | JSON (str 기반) |
Pickle (bytes 기반) |
변환 방법
str → bytes
:.encode(encoding)
bytes → str
:.decode(encoding)
일반적인 경우 str
을 사용하지만, 네트워크, 암호화, 바이너리 데이터 처리 등 특정 상황에서는 bytes
가 필수적이다.
1-4. C 스타일 문자열 포맷팅 대신 f-string 사용
기존 방법과 문제점
4가지의 방법이 있지만, f-string 쓰는게 좋음. 그래도 나머지 방법이 왜 안좋은지는 알아야.
1. % 연산자 방식 (C 스타일)
name = "Alice"
age = 25
print("이름: %s, 나이: %d" % (name, age))
문제점
- 가독성이 떨어짐
- 값 변경이 어렵고 순서에 의존적
- 같은 값을 여러 번 사용할 경우 중복 필요
2. .format() 방식
print("이름: {}, 나이: {}".format(name, age))
문제점
- 위치 인덱스가 필요하여 코드가 길어짐
- 중괄호
{}
안에 변수명을 명시해야 가독성이 높아짐
f-string 방식 (추천)
name = "Alice"
age = 25
print(f"이름: {name}, 나이: {age}")
장점
- 직관적이고 간결함
- 성능 우수
- 타입 변환이 쉬움
- 중괄호
{}
안에 변수명 직접 입력 가능
가능하면 f-string
을 사용하여 문자열을 포맷팅하는 것이 바람직하다.
1-5 복잡한 식을 쓰는 대신 도우미 함수를 작성하라
- 핵심 개념: 코드의 가독성을 높이기 위해 복잡한 식을 한 줄로 작성하기보다 도우미 함수를 사용하라.
- 실천 방법:
- 같은 로직이 반복될 경우 도우미 함수를 만들어 재사용하라.
or
,and
같은 연산자보다if/else
표현식을 활용하면 가독성이 좋아진다.- DRY(Don’t Repeat Yourself) 원칙을 따라 중복을 줄이자.
기억해야 할 내용
- 파이썬 문법을 사용하면 아주 복잡하고 읽기 어려운 한 줄짜리 식을 쉽게 작성할 수 있다.
- 복잡한 식을 도우미 함수로 옮겨라. 특히 같은 로직을 반복해 사용할 때는 도우미 함수를 꼭 사용하라.
- 불 연산자나 or나 and를 식에 사용하는 것보다 if/else 식을 쓰는 편이 더 가독성이 좋다.
1-6 인덱스를 사용하는 대신 대입을 사용해 데이터를 언패킹하라
- 핵심 개념: 파이썬은 한 문장에서 여러 값을 할당할 수 있는 언패킹 기능을 제공한다.
- 실천 방법:
- 튜플이나 리스트를 언패킹하여 변수에 할당하면 코드가 깔끔해진다.
- 인덱스를 사용해 값을 추출하기보다 언패킹을 활용하면 시각적인 잡음이 줄어든다.
- 중첩된 이터러블에서도 언패킹을 사용할 수 있다.
1-7 range 보다는 enumerate를 사용하라
- 핵심 개념:
enumerate
를 활용하면 루프에서 인덱스와 값을 동시에 얻을 수 있다. - 실천 방법:
range(len(list))
대신enumerate(list)
를 사용하면 가독성이 향상된다.enumerate(iterable, start=n)
을 활용하면 원하는 숫자부터 인덱스를 시작할 수 있다.enumerate
는 지연 계산을 수행하는 제너레이터 방식으로 동작한다.
예제 코드:
items = ['감자칩', '팝콘']
for i, value in enumerate(items):
print(f'{i}: {value}')
결과:
0: 감자칩
1: 팝콘
1-8 여러 이터레이터에 대해 나란히 루프를 수행하려면 zip을 사용하라
- 핵심 개념: 여러 개의 리스트나 이터러블을 동시에 순회할 때
zip
을 활용하면 코드가 간결해진다. - 실천 방법:
zip(iter1, iter2, ...)
을 사용하면 여러 리스트의 요소를 튜플로 묶어서 순회할 수 있다.- 가장 짧은 리스트의 길이에 맞춰 루프가 종료된다.
- 리스트 길이가 다를 경우
itertools.zip_longest
를 사용하면 긴 리스트도 모두 처리할 수 있다.
예제 코드:
names = ['철수', '영희', '민수']
scores = [90, 85, 95]
for name, score in zip(names, scores):
print(f'{name}의 점수는 {score}점입니다')
결과:
철수의 점수는 90점입니다
영희의 점수는 85점입니다
민수의 점수는 95점입니다
- 길이가 다른 이터러블을 처리하려면
itertools.zip_longest
를 사용한다.
import itertools
for name, score in itertools.zip_longest(names, scores, fillvalue='점수 없음'):
print(f'{name}의 점수는 {score}점입니다')
기억해야 할 내용:
zip
은 여러 이터레이터를 나란히 순회하는 간결한 방법이다.- 기본적으로 가장 짧은 리스트까지만 처리되므로, 길이가 다른 경우
zip_longest
를 활용하라. zip
은 지연 계산 방식으로 동작하므로, 메모리 효율이 좋다.
1-9 for나 while 루프 뒤에 else 블록을 사용하지 말라
- 핵심 개념:
else
블록은for
나while
루프에서break
가 발생하지 않은 경우 실행된다.
if/else 문에서 else는
이 블록 앞의 블록이 실행되지 않으면 이 블록을 실행하라
는 뜻.
내가 착각한 것 → else가 처리할 예외가 없는 경우에 이 블록을 실행하라는거라 생각했음.
- 문제점:
else
블록이 루프와 연관된 동작처럼 보이지만, 실제 동작 방식이 직관적이지 않아 혼동을 초래할 수 있다.else
없이 코드의 흐름을 더 명확하게 작성하는 것이 좋다.
예제 (else 사용):
for num in range(10):
if num == 5:
print("Found 5!")
break
else:
print("5를 찾지 못했습니다.") # 5를 찾았으면 실행되지 않음
기억해야 할 내용:
for
나while
뒤에else
블록을 사용하면break
발생 여부에 따라 실행되지만, 코드 흐름이 직관적이지 않다.else
를 사용하지 않고flag 변수
를 활용하거나 단순한 논리로 처리하는 것이 가독성을 높인다.
1-10 대입식을 사용해 반복을 피하라
- 핵심 개념: 왈러스 연산자(
:=
, assignment expression)를 사용하면 한 줄에서 값의 할당과 평가를 동시에 수행할 수 있다. - 장점:
- 반복되는 코드 제거: 한 번 할당한 값을 다시 평가하지 않아도 됨.
- 더 간결한 코드 작성 가능:
if
,while
문에서 값 할당과 조건 평가를 동시에 수행 가능.
예제 1: 기존 방식
def process_number():
user_input = input("숫자를 입력하세요: ")
number = int(user_input)
if number != 0:
print(f"입력한 숫자 {number}의 제곱은 {number ** 2}입니다")
예제 2: 왈러스 연산자(:=) 사용
def process_number():
if (number := int(input("숫자를 입력하세요: "))) != 0:
print(f"입력한 숫자 {number}의 제곱은 {number ** 2}입니다")
예제 3: while 문에서 활용
기존 방식:
data = input("입력: ")
while data:
print("입력한 값:", data)
data = input("입력: ")
왈러스 연산자 사용:
while (data := input("입력: ")):
print("입력한 값:", data)
기억해야 할 내용:
- 왈러스 연산자(
:=
)를 사용하면 값을 할당하면서 동시에 평가할 수 있다. if
,while
조건문에서 값을 한 번만 가져와서 중복을 줄일 수 있다.- 대입식이 큰 표현식의 일부로 사용될 경우 괄호로 감싸야 한다.
- 파이썬에서는 switch/case 문이나 do/while 루프를 쓸 수 없지만, 대입식을 사용하면 이런 기능을 더 깔끔하게 흉내 낼 수 있다.
반응형
'리뷰 > 책' 카테고리의 다른 글
[파이썬 코딩의 기술] - 3장 함수 (0) | 2025.02.14 |
---|---|
[파이썬 코딩의 기술] - 2장 리스트와 딕셔너리 (0) | 2025.02.14 |
Do it! 첫 코딩 - 5장 (0) | 2025.02.14 |
Do it! 첫 코딩 - 4장 (0) | 2025.02.14 |
Do it! 첫 코딩 - 3장 (0) | 2025.02.14 |