코드 재사용
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 안에 있지만, self나 cls에 대한 정보와 관련 없이 사용한다.
인자를 정의할 때, 첫 인자로 self나 cls를 쓰지 않는다.
class Calculator:
@staticmethod
def add(a, b):
return a + b
# 객체 만들지 않아도 바로 사용 가능
print(Calculator.add(10, 20))
singleton desing pattern #python
어떤 클래스의 인스턴스가 프로그램 전체에서 오직 하나만 존재하도록 보장하는 디자인 패턴
why?
여러 곳에서 동시에 접근하면 안되거나, 자원을 공유해야 하는 경우에 필요하다.
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')