2026.04.19(Sun)

오유찬·2026년 4월 24일

DE

목록 보기
12/16

OOP

  • 코드 재사용

  • DRY : 반복하지 말 것

    Python Class Decorator #python


python에서 decorator는 호출 가능한 객체(Callable)를 받아서 다른 객체를 반환하는 함수

  • 기존 기능을 수정하지 않고 새로운 기능을 추가

class decorator는 클래스 자체를 수정하거나 확장할 때 사용

  • __init__ , __call__
class CallCounter:
    def __init__(self, func):
        self.func = func
        self.count = 0  # 호출 횟수를 저장하는 상태

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} 함수가 {self.count}번 호출되었습니다.")
        return self.func(*args, **kwargs)

@CallCounter
def say_hello():
    print("안녕하세요!")

say_hello()
say_hello()

sayl_hello가 실행될 때마다, self.count 1씩 올리며 상태 유지

class method #python


python에서 일반 메서드는 첫 번째 인자로 self를 받는다. → self는 이미 만들어진 구체적인 객체(인스턴스)

  • 일반 메서드 사용하려면 이미 객체 = class() 를 통해 이미 객체가 존재해야 한다.

IF, 직원 데이터를 파일에서 읽어와서, 그 데이터를 바탕으로 직원을 새로 만들고 싶다면?

  • 직원을 만드려고 하는데
  • 아직 직원 객체가 존재하지 않는다.
  • 일반 메서드에서는 '존재하는 직원'이 수행하는 동작
  • 존재하지 않는 객체에 메서드를 수행시킬 수는 없다!
  • @classmethod 등장

class method는 객체가 없어도 클래스(설계도)만 있으면 호출할 수 있다.

자동차 회사에 전화 걸어서 직원 파일을 보낼 테니 자동차(객체)를 뽑아주세요!
자동차 객체는 없지만 자동차 회사 클래스는 존재한다!

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    # 일반 메서드는 불가능
    def from_file_instance(self, filename):
        # 이 메서드를 부르려면 이미 Employee 객체가 있어야 하는데,
        # 우리는 객체를 '만들기 위해' 이 기능을 쓰려는 거라 모순이 생겨요.
        pass

    # 클래스 메서드는 가능!
    @classmethod
    def from_file(cls, filename):
        # 파일 읽기 로직 (생략)
        name, salary = "철수", 50000 
        # cls는 Employee 클래스 그 자체이므로, 여기서 객체를 생성해서 반환합니다.
        return cls(name, salary) 

# 객체가 하나도 없는 상태에서도 호출 가능!
new_emp = Employee.from_file("info.txt")

일반 메서드는 만들어진 객체가 하는 행동이고, 클래스 메서드는 객체를 만들기 전에도 클래스가 할 수 있는 행동 → 대체 생성자(파일로 만들기, JSON으로 만들기 …)는 클래스 메서드로 만드는 것이 논리적으로 좋다.

class Person:
	CURRENT_YEAR = 2024
	def __init__(self, name, age):
		self.name = name
		self.age = age
		
	# Add a class method decorator
	@classmethod
	# Define the from_birth_year method
	def from_birth_year(cls, name, birth_year):
	# Create age
	age = Person.CURRENT_YEAR - birth_year
	# Return the name and age
	return cls(name, age)

bob = Person.from_birth_year("Bob", 1990)

self는 일반 메서드에서 나중에 생성될 실제 객체를 담기 위한 빈 그릇과 같다.
일반 메서드 호출 시 파이썬 내부 실행 동작은 다음과 같다.
1. emp = Employee() 객체 탄생
2. emp.general_method() 호출
3. 파이썬이 자동으로 Employee.general_method(emp)로 변환해서 실행 → 객체 emp를 self 자리에 넣어준다.

그래서, 일반 메서드는 실행되는 순간에 반드시 self 자리에 들어갈 실제 객체가 메모리에 살아있어야만 작동한다.

Employee.general_method()처럼 호출하면 self 자리에 객체를 넣어줘야 하는데, 전달된 객체가 아무것도 없기 때문에 self에 뭘 넣어서 실행할 지 모르기에 에러가 발생한다.

그렇기 때문에, ~={cyan}파일을 읽는 로직의 함수가 클래스 내부에 써 있더라도, 그 메서드 형식이 self 인자를 받는 일반 메서드라면 객체가 생겨야만 쓸 수 있는 기능=~이다

class method가 가능한 이유는 self 대신 인자로 cls를 받기 때문이다.

  • 일반 메서드 : self가 있어야만 기능 수행
  • 클래스 메서드 : self가 아니라 cls(class 설계도)만 있으면 기능 수행 가능

그래서 파일로부터 객체를 만드는 from_file 같은 기능은 객체가 생성되기 전이기 때문에, self 인자가 아니라 class method정적 메서드(staticmethod)로 만들어야만 호출이 가능하다!!

static method(정적 메소드) #python


class 안에 있지만, selfcls에 대한 정보와 관련 없이 사용한다.
인자를 정의할 때, 첫 인자로 selfcls를 쓰지 않는다.

  • 클래스 이름으로 바로 호출 가능
  • 논리적으로 클래스와 관련 있는 편의 기능 묶어둘 때 사용
class Calculator:
	@staticmethod
	def add(a, b):
		return a + b

# 객체 만들지 않아도 바로 사용 가능
print(Calculator.add(10, 20))

singleton desing pattern #python


어떤 클래스의 인스턴스가 프로그램 전체에서 오직 하나만 존재하도록 보장하는 디자인 패턴

why?
여러 곳에서 동시에 접근하면 안되거나, 자원을 공유해야 하는 경우에 필요하다.

  • DB 연결 객체 : 연결을 여러 개 만들면, 서버 부하가 커지기 때문에 하나만 만든다.
  • Settings 관리자 : 프로그램 설정값은 하나로 통일되어야 한다.
  • Logging 객체 : 로그를 기록하는 통로를 하나로 통일할 때 쓴다.
def singleton(cls):
	# 생성될 객체를 담아둘 저장소
	instances = {} 
	
	def get_instance(*args, **kwargs):
		if cls not in instances:
			instances[cls] = cls(*args, **kwargs)
		
		return instances[cls]
		
	return get_instance
	
@singleton
class Database:
	def __init__(self):
		print('connected database')

db1 = Database()
db2 = Database()

print(db1 is db2) # resutl = True

데코레이터로 구현하는 방법 이외에도, 클래스 자체의 생성 과정을 제어하는 __new__ 매직 메서드를 사용할 수 있다.

class SingletonClass:
	_instance = None
	
	def __new__(cls, *args, **kwargs):
		if not cls._instance:
			# 인스턴스가 없을 때만 부모 클래스의 __new__를 호출해 생성
			cls._instance = super().__new__(cls)

팩토리 패턴 #python


어떤 객체를 만들지 결정하는 로직을 별도의 클래스나 함수에 둔다.

  • 유연성 : 나중에 새로운 종류의 객체가 추가되어도, 메인 코드를 수정하지 않고 팩토리 객체에 추가하면 된다.
  • 복잡함 은폐 : 객체를 만드는 과정이 복잡할 때(여러 설정 거칠 때), 사용자가 이를 모른 상태로도 할 수 있도록 한다.
    (식당에 가서 레시피를 모르고 메뉴를 시킬 수 있는 것처럼)
class Dog:
	def speak(self):  return "wow"
class Cat:
	def speak(self): return 'yaong'
	
class PetFactory:
	@staticmethod
	def get_pet(pet_type):
	pets = {"dog": Dog, "cat": Cat}
	return pets.get(pet_type, Dog)()
	
my_pet = PetFactory.get_pet("cat")
print(my_pet.speak())

factory method patter 확장
if-else, dictionary로 객체를 찍어내는 것을 넘어서 공장 자체를 추상화하는 방식이 있다.
이를 factory method pattern이라고 한다.

from abc import ABC, abstractmethod

class Animal(ABC):
	@abstractmethod
	def speak(self):   pass

class JindoDog(Ainmal):
	def speak(self):   return '진돗개 멍'

class Spasal(Animal):
	def speak(self):   return '삽살개 멍'
	
class AnimalFactory(ABC):
	@abstractmethod
	def create_method(self):   pass
	
	def deliver(self):
		animal = self.create_animal()
		print(f" {animal.speak() = }")

class SeoulFacotry(AnimalFactory):
	def create_animal(self):  return JindoDog()

class BusanFactory(AnimalFactory):
	def create_animal(self):   return Sapsal()
	
	
seoul = SeoulFactory()
seoul.deliver()

→ 객체 생성 로직 분리되어 있어, 새로운 종류의 객체가 추가되어도 기존 코드를 수정하기가 편하다.

Abstract class(추상 클래스) #python


상속받는 자식 클래스들이 반드시 구현해야 할 메서드를 지정해 주는 가이드라인

  • 자기 자신으로는 객체를 만들 수 없지만, 다른 클래스들이 공통적으로 가져아 할 특정을 정의할 때 사용한다.

왜 미완성 클래스를 만들고, abc module이 필요한가?

  • 협업할 때, 공통적으로 지켜야 할 규칙을 강제하기 위함이다.
from abc import ABC, abstractmethod

class Animal(ABC): # ABC를 상속받으면 추상 클래스가 된다.
	@abstractmethod
	def move(self):
		"""animal이라면 반드시 move 기능이 있어야 한다."""
		pass

my_animal = Animal() # 추상 클래스는 직접 객체를 만들 수 없으므로 에러 발생

class Human(Animal):
	def move(self):
		print('두 발로 걸음")
	
class Fish(Animal):
	def move(self):
		print('지느러미로 헤엄침')

h = Human()
h.move()

만약 move() 메서드를 정의하지 않으면 Can't instantiate abstract class Fish with abstract method move 라는 에러를 띄운다.

파이썬에는 다른 언어처럼 별도의 interface 키워드는 없지만, 추상 클래스를 이용해 그 역할을 수행한다.

  • 추상 클래스 : 자식 클래스는 부모 클래스의 기능을 쓰고, 필요한 건 추가로
  • 인터페이스(형태) : 강제 메서드만 존재(기능은 없고 형태만 갖춤)
from abc import ABC, abstractmethod

# python style's interface
class Remocon(ABC):
	@abstractmethod
	def turn_on(self):   pass
	def turn_off(self):  pass
	
# functions - implement interface
class TV(Remocon):
	def turn_on(self): print('turn on tv')
	def turn_off(self): print('turn off tv')

class Airconditioner(Remocon):
	def turn_on(self): print('turn on air')
	def turn_off(self): print('turn off air')
profile
열심히 하면 재밌다

0개의 댓글