반응형

 

오늘 다뤄볼 주제는 팩토리 메소드 패턴입니다.

 

이 디자인의 특징은 클라이언트 코드 수준에서 객체를 직접 생성하지 않고, 팩토리 메소드에게 위임합니다.

 

이렇게 하면 클라이언트 코드와 사용하는 객체간의 거리를 만들어 유연성과 유지관리성을 높일 수 있게 되는데, 이런 표현을 책에서는 loose coupling(느슨한 결합)이라고 합니다.

 

아래 팩토리 메소드 패턴의 주요 구성요소와 함께 예제코드를 작성해보겠습니다

 

 

구성 요소 

1) Generator = abstract class or interface 

2) SubClass of Generator = Product 유형의 오브젝트를 생성할 팩토리 메소드가 구현될 클래스

3) Product = abstract class or interface

4) SubClass of Product = 팩토리 메소드에 의해 생성되는 오브젝트의 클래스

 

 

 

 

예제 코드 설명

- (1)Generator 는 팩토리 메소드가 구현될 추상화 클래스이며, ABC 클래스를 상속받아 Base 클래스의 메소드의 구현을 강제화 합니다

- (2) SubClass 는 Generator 클래스를 상속받아, 유형별 데이터 수집기 클래스의 객체를 생성하는 팩토리 메소드를 구현합니다.

- (3) Product 는 데이터 수집기의 추상화 클래스입니다.

- (4) SubClass 는 Product 클래스를 상속받아, 유형별 데이터 수집기의 동작을 구현합니다.

- (5) create_collector 함수는 유형별 데이터 수집기의 객체를 생성하는 팩토리 메소드를 구현합니다.

 

 

예제 코드 

from abc import ABC, abstractmethod





class Collector(ABC):

    @abstractmethod

    def collect(self, data):

        pass







class DBCollector(Collector):

    def collect(self, data:str):

        print(f"Collecting data from database: {data}")





class APICollector(Collector):

    def collect(self, data:str):

        print(f"Collecting data from API: {data}")





class Generator(ABC):

    

    @abstractmethod

    def generate(self):

        pass



class DBCollectorGenerator(Generator):

    

    def generate(self):

        return DBCollector()

    

class APICollectorGenerator(Generator):



    def generate(self):

        return APICollector()

    



def create_collector(name:str):

    generator = {

        "db" : DBCollectorGenerator, 

        "api" : APICollectorGenerator

    }



    return generator[name]().generate()





def main():

    db_collector = create_collector("db")

    api_collector = create_collector("api")



    target_data = ["user_information","product_information"]



    for collecting_data in target_data:

        db_collector.collect(collecting_data)

        api_collector.collect(collecting_data)



if __name__ == "__main__":

    main()

 

 

출력 결과 

Collecting data from database: user_information

Collecting data from API: user_information

Collecting data from database: product_information

Collecting data from API: product_information

 

 

만약 제가 카카오 API 를 연동해서 어떤 데이터를 수집해야 하는 요구상황이 생긴다고 가정해볼게요. 

 

저는 위 구조에서 KaKaoAPICollector 클래스를 추가하고 collect 메소드를 구현할거에요.

 

기존 코드의 영향을 적게 주면서 새로운 클래스만 추가하면 되기 때문에, 이런 부분에서 유연성과 확장성이 올라갔다고도 볼 수 있을 겁니다.

 

 

.

 

 

 

반응형
반응형

안녕하세요. 오늘은 이터레이터에 대해 얕게 배워보겠습니다.

 

iteration 은 (계산 컴퓨터 처리 절차의)반복 이라는 뜻을 갖습니다. 

 

프로그래밍 언어에서 이터레이션은 여러 객체를 담고 있는 반복 가능형 객체(list,set,dictionary)의 요소들을 하나 하나씩 순회하며 꺼낼 수 있는 인터페이스라고 볼 수 있습니다.

 

우리가 파이썬을 공부하면 처음에 배우게 되는 반복문 for 도 구현체 안쪽에서는 iter, __next__() 를 호출하고 있습니다. iter()로 이터레이터 객체를 만들어 next()를 호출하면서 이터레이터 객체를 돌리고, 요소가 없을 때 StopIteraton 예외를 일으켜서 for 루프 종료를 알립니다.

 

직접 for 루프가 되서 호출해보면 다음과 같습니다.

 

test = 'abc'



iterator = iter(test)



next(test)

>> a



next(test)

>> b



next(test)

>> c



next(test)

>> d



next(test)

StopIteration

 

그럼 위에 배운 개념을 이용해서, 요소를 거꾸로 꺼내는 이터레이터 객체를 만들어서 반복문을 돌려보겠습니다.

 

class Reverse:



    def __init__(self,data):

        [self.data](http://self.data) = data

        self.index = len(data)



    def __iter__(self):

        return self



    def __next__(self):

        if self.index == 0 :

            raise StopIteration

        self.index -=1

        return self.data[self.index]

    

rev = Reverse('Hello World')



for c in rev:

    print(c)
 

 

끝.

반응형
반응형

안녕하세요.  오늘의 주제는 함수를 객체처럼 다루기 입니다.

 

 

일단 아래와 같이 factorial 함수를 간단히 작성해보겠습니다.

 

def factorial(n):

    

    if n < 2 :

        return 1

    

    return n * factorial(n-1)



print(factorial(3))

print(factorial.__doc__)  # return n!

 

아래 처럼 함수 객체를 새로운 fact 라는 변수에 할당하고, 이 변수명을 통해 함수를 호출해볼 수 있습니다. 

 

fact = factorial

fact(5)

 

 

이번엔 factorial 을 map() 을 써서 호출해보겠습니다. 

(참고로 map 함수는 반복 가능한 객체를 2번째 요소로 넣어, 1번째 함수의 적용한 결과를 가지는 반복 가능형 객체를 반환합니다.)

 

map(fact,range(5)) -> 0 부터 4까지의 리스트

 

이렇게 하면 map의 주소가 출력되기 때문에, 가시적으로 확인하기 위해 list 로 형변환 해보겠습니다.

list(map(factorial,range(5))) 
-> output [1, 1, 2, 6, 24]

 

 

 위와 같이 구현하는 스타일을 함수형 프로그래밍이라고 부릅니다.

 

 

반응형
반응형

 

오늘은 운영 수준에서 따르면 좋은 파이썬 코딩 규칙들 3가지 정도만 소개해드리겠습니다. 출처는 블로그나 책에서 참고했습니다.

 

 

 

(1) 클래스의 내부 메소드는 _(언더바) 로 시작하는 함수명을 짓는다.

 

(2) 주석 코멘트를 잘 작성해준다.

 

(3) iteration 같은 반복자 루프에서는 null 값을 고려한 방어 코드를 작성한다.

 

 

그럼 짧게 코드로 작성해보면 아래와 같습니다.

class MyClass:

    def run(self, my_list: list[str]):

        for something in my_list or []:              (3) iterating 같은 반복자 루프에서는 null 값을 고려한 방어 코드를 작성한다.  

            self._do_something(something)

        

    

    def _do_something(self,something):   (1) 클래스의 내부 메소드는 _(언더바) 로 시작하는 함수명을 짓는다.

        print(something)

    

    def _clean() -> None:     (2) 주석 코멘트를 잘 작성해준다.



       """write clean code .. """



obj = MyClass()

obj.run(["game","movie","runing"])

 

 

 

 

 

감사합니다. 

 

 

 

.

 

 

 
반응형
반응형

파이썬에서도 객체지향 프로그래밍(OOP) 의 중요성과 코딩 습관을 강조하곤 합니다.

클래스 내에 메소드를 구현할 때 파이썬에서는 3가지 유형을 제공합니다.

class method, intance method, static method 인데요.

 

아래 3가지 유형을 정리해봅니다.

 

- class method
    - 클래스에 바인딩되는 함수
    - 클래스의 상태를 수정하여 모든 인스턴스에 적용될 수 있는 함수
- instance method
    - self 를 첫 번째 인자로 받는 메소드
    - 클래스의 인스턴스를 “암시적”으로 입력받아 클래스 속성과 상호 작용할 수 있습니다.
    - 인스턴스 내  데이터를 액세스 할 수 있고, 수정할 수 있습니다.
- static method
    - 데코레이터를 이용해 정의합니다. 
    - 클래스의 인스턴스가 아닌 클래스에 속합니다.
    - 특정 클래스의 컨텍스트 내에서 의미가 있는  함수를 만들 때 정의합니다.

 

 

이중에서 class method 를 유틸리티 성 함수를 구현할 때 사용하면 좋은데요. 아래 예를 들어보겠습니다.

import pandas as pd
class DataETLProcessor:
    def __init__(self,data):
        self.data =data

    def process_etl(self):
        #write etl process logic code
        pass

    def from_csv_by_instance_method(self,filepath):
        self.data = pd.read_csv(filepath)

    @classmethod
    def from_csv_by_class_method(cls,filepath):
        data = pd.read_csv(filepath)
        return cls(data)

    @classmethod
    def from_json(cls,filepath):
        data = pd.read_json(filepath)
        return cls(data)
    
# case 1 실행 예제)
etl = DataETLProcessor()
etl.from_csv_by_instance_method("./sample_data.csv")
etl.process_etl()

# case 2실행 예제)
etl = DataETLProcessor.from_csv_by_class_method("./sample_data.csv")
etl.process_etl()

 

 

case 1는 instance method 를 이용한 호출이고, case2 는 class method 를 이용한 호출인데, 한 줄 줄었지만 더 간결해졌어요.

그리고 instance 와 class 관련된 변수와 기능, 로직을 분리하면 코드가 깔끔해집니다. 위에서 from_json , from_parquet 그리고 아예 파경로에서 파일 타입을 읽어 포맷별로 데이터를 읽을 수 있도록 구현할 수도 있구요.

 

프로그램의 볼륨이 커질수 있다면 유지보수 용이하게 저렇게 분리해서 구현하는 습관을 들여도 좋겠습니다.

 

물론 간단한 프로그램이라면 그냥 편한대로 구현하는게 더 좋을 수 있습니다.

반응형
반응형

오늘도 간단한 파이썬 코딩을 소개해드리겠습니다.

 

k,v 타입의 파이썬 딕셔너리 타입을 쓸 때, 아래 처럼 키의 유무로 인한 분기를 태우는 코드를 많이 작성하실 거에요. 

 

def use_get_dict():
    my_dict={}
    my_dict['a'] = 1
    my_dict['b'] = 2
    my_dict['c'] = 3

    # 일반적인 방법
    if 'a' in my_dict:
        v = my_dict['a']
        print(f"value is {v}")
    else:
        v = 0
        print("It doesn't have key")
    

딕셔너리 타입의 get () 을 이용해서 동일한 분기 처리를 해보겠습니다.

def use_get_dict():
    my_dict={}
    my_dict['a'] = 1
    my_dict['b'] = 2
    my_dict['c'] = 3

    # dict.get() 을 이용한 방법
    v = my_dict.get('a',0)
    print(f"value is {v}")

 

코드가 조금 간결해졌어요.

 

이번에는 swap 을 해볼게요. 예를 들어 정렬을 구현할 때 두 변수의 값을 교환하는 코드를 보통 이렇게 작성할 텐데요. 

 

def swap_with_temp():
    a = 1
    b = 2
    temp = a
    a = b
    b = temp 

    print(f"a = {a}  b={b}")

 

이번에는 아래 예제 코드를 통해서 temp 변수 없이 a, b 의 변수 값을 교환해보겠습니다.

def swap_without_temp():
    a = 1
    b = 2
    a, b = b, a
    print(f"a = {a}  b={b}") 

 

코드가 조금 더 쉬워보여요

 

오늘은 여기까지하겠습니다.

 

끝.

반응형

+ Recent posts