티스토리 뷰

0. 클래스와 함수

- 함수는 동작만

- 클래스는 함수+상태 저장 가능

 

1. 상속 : 같은 기능인데 이름만 다름 => 중복 방지

 

- 예: 좋아요, 싫어요 기능

class Like:
	def __init__(self, post, user):
    	self.post = post
        self.user = user
        
class Sad:
	def __init__(self, post, user):
    	self.post = post
        self.user = user
        
        
# 공통된 부모 클래스를 만들어줌
class React:
	def __init__(self, type, post, user):
    	self.type = type
    	self.post = post
        self.user = user
        
        
 # like와 sad는 react를 상속 받음
 class Like(React):
 	def __init__(self, post, user):
    	super().__init__("LIKE", post, user)
        
 class Sad(React):
 	def __init__(self, post, user):
    	super().__init__("SAD", post, user)

 

2. 추상적인 부모 클래스 : 직접 인스턴스를 만들지 않고, 자식 클래스를 만들때만 사용(상속할 때만 사용), 의미를 갖지 X

- 파이썬은 추상적인 부모 클래스를 나타낼 방법이 없다 => 개발자의 몫으로 남겨둠 (Java에서는 abstract 클래스)

* 파이썬은 빠르게 프로그래밍 할 수 있는 대신, 많은 약속을 생략함

- 왜 이렇게 할까? 전체적인 코드의 구조를 유지하기 위해

1) 명확한 역할 분리, 구조화: 추상 클래스는 "공통 구조", 자식 클래스는 "구체 행동"

# 추상적인 부모클래스를 이용해 인스턴스를 만들지 않음
## 이렇게 안씀
reaction = Reaction("sdfksdfls", post, me)

# 반드시 구체적인 자식 클래스로 사용
like = Like(post, me)

2) 유지보수: 오타가 나거나 의미없는 문자를 입력해도 코드상에 에러가 없어 디버깅이 어렵다

# 예시
reaction = Reaction("Liek", post, me)  # 오타났는데 코드상 에러 없음

 

3) 확장성: 메서드가 달라야 한다면, if문을 잔뜩 써야하는 등의 문제가 있으나 클래스로 나누면 깔끔하게 분리 가능

class Like(Reaction):
    def display(self):
        return "좋아요"

class Dislike(Reaction):
    def display(self):
        return "싫어요"

 

3. reaction = Reaction("sdfksdfls", post, me) 식으로 구현했을때

class Reaction:
    def __init__(self, kind, post, user):
        self.kind = kind
        self.post = post
        self.user = user

    def display(self):
        if self.kind == "Like":
            return "좋아요"
        elif self.kind == "Dislike":
            return "싫어요"
        else:
            return "알 수 없음"


reactions = [
    Reaction("Like", "Post1", "UserA"),
    Reaction("Dislike", "Post1", "UserB"),
    Reaction("Liek", "Post1", "UserC")  # 오타인데도 실행됨! 버그 발생
]

for r in reactions:
    print(r.display())

 

4. 객체지향 방식: 자식 클래스로 분리

from abc import ABC, abstractmethod

class Reaction(ABC):
    def __init__(self, post, user):
        self.post = post
        self.user = user

    @abstractmethod # 틀만 있고 내용은 없음 => 자식 클래스에서 구현해야함
    def display(self):
        pass

class Like(Reaction):
    def display(self):
        return "좋아요"

class Dislike(Reaction):
    def display(self):
        return "싫어요"

reactions = [Like("Post1", "UserA"), Dislike("Post1", "UserB")]

for r in reactions:
    print(r.display())

 

5. @abstractmethod (추상메서드)

- 자식 클래스가 반드시 구현해야하는 메서드를 지정

- 왜 쓸까? 클래스를 설계도처럼 만들고 싶을때, 공통 메서드와 이름은 정해두되, 행동은 자식이 정하게 하고 싶을때

- @abstractmethod를 사용하면, 부모 클래스로 인스턴스 생성할 수 없고, 자식클래스에 해당 메서드를 구현해야함

from abc import ABC, abstractmethod

class Reaction(ABC):
    def __init__(self, kind, post, user):
        self.kind = kind
        self.post = post
        self.user = user
    
    @abstractmethod
    def sound(self):
        pass

# a = Reaction("Good","좋아","goody") # error , 자식 클래스 생성해야함

class Like(Reaction):
    def __init__(self, post, user):
        super().__init__("LIKE", post, user)

# b = Like("좋아용","하잉") # 에러, 자식클래스에 sound 정의해줘야 함

class Sad(Reaction):
    def __init__(self, post, user):
        super().__init__("SAD", post, user)
    
    def sound(self):
        return "흑흑"

c = Sad("슬픈날","닉네임")
print(c.sound()) # 흑흑

 

6. 보통은 같은 메소드를 쓰는데 간혹 예외가 필요한 경우:오버라이딩(부모 함수 수정)

class Logger:
    def log(self, message):
        print(f"[LOG] {message}")  # 기본 로그는 콘솔 출력

class FileLogger(Logger):
    def log(self, message):
        with open("log.txt", "a") as f:
            f.write(f"[FILE] {message}\n")  # 파일에 저장

 

7. 클래스 변수(모든 인스턴스가 공유)와 인스턴스 변수(인스턴스 내에서 독립적으로 사용)

class Test:
    num = 0  # 클래스 변수

    def wrong_change(self):
        num = 5  # wrong_chagen 내에서만 쓰는 지역 변수임

    def instance_change(self):
        self.num = 6  # 값을 직접 대입=> 인스턴스 변수가 새로 생성됨

    def class_change(self):
        Test.num = 7  # 클래스 변수 바뀜(인스턴스 아님)

a = Test()
b = Test()
a.wrong_change() # class num에는 아무일도 안일어남
print(a.num) # 0
print(b.num) # 0
a.instance_change() # 인스턴스 a의 값 바꿈 => a의 num은 더이상 클래스변수가 아니고 인스턴스 변수임
print(a.num) # 6 
print(b.num) # 0
a.class_change()
print(a.num) # 6
print(b.num) # 7

 

8. 클래스 내의 리스트

class Test:
    data = []  # 클래스 변수

    def wrong_change(self):
        item = "apple"  # wrong_chagen 내에서만 쓰는 지역 변수임
        data = [item]

    def instance_change(self):
        self.data = []  # 인스턴스 전용 리스트가 생성됨
        self.data.append("banana")

    def class_change(self):
        Test.data.append("carrot") # 클래스 변수 바뀜(인스턴스 아님)

a = Test()
b = Test()
a.wrong_change() # class data에는 아무일도 안일어남
print(a.data) # []
print(b.data) # []
a.instance_change() # 인스턴스 a의 data 수정 => a는 더이상 data를 클래스 data를 쓰지않고 독립적으로 유지
print(a.data) # ['banana'] # 인스턴스를 수정한
print(b.data) # []
a.class_change()
print(a.data) # ['banana']
print(b.data) # ['carrot']

- 만약 instance_change에 self.data = [ ] 로 초기화 하지 않는다면?

class Test:
    data = []  # 클래스 변수

    def wrong_change(self):
        item = "apple"  # wrong_chagen 내에서만 쓰는 지역 변수임
        data = [item]

    def instance_change(self):
        self.data.append("banana") # self.data =[]로 초기화하지 않고 "변경"만해 클래스 변수를 가리킴

    def class_change(self):
        Test.data.append("carrot") # 클래스 변수 바뀜(인스턴스 아님)

a = Test()
b = Test()
a.wrong_change() # class data에는 아무일도 안일어남
print(a.data) # []
print(b.data) # []
a.instance_change() # 클래스 리스트 수정
print(a.data) # ['banana'] 
print(b.data) # ['banana']
a.class_change()  # 클래스 리스트 수정
print(a.data) # ['banana', 'carrot']
print(b.data) # ['banana', 'carrot']

- 정리: 대입(=)하면 인스턴스 속성이 새로 생기고, 수정(append, update 등)만 하면 기존 클래스 변수를 그대로 공유

class Test:
    num = 0
    data = []

a = Test()
b = Test()
a.num = 5        
a.data.append(1) 

print(a.num) # 5
print(b.num) # 0

print(a.data) # [1]
print(b.data) # [1]

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함