반응형

 

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

 

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

 

이렇게 하면 클라이언트 코드와 사용하는 객체간의 거리를 만들어 유연성과 유지관리성을 높일 수 있게 되는데, 이런 표현을 책에서는 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 메소드를 구현할거에요.

 

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

 

 

.

 

 

 

반응형
반응형

 

 

오늘 다뤄볼 주제는 덕 타이핑입니다.

 

 

덕 타이핑은 프로그래밍 언어에서 클래스의 행동에 중점을 둔 컨셉중 하나에요. 

 

클래스의 상속, 유형보다 객체의 동작 즉 행동을 강조해서 유연성을 높이고 캐스팅의 필요성을 줄여주는 프로그래밍 스타일입니다.

 

아래 코드로 예를 들어 보겠습니다.

 

class Duck:

    def sound(self):

        print("Quack!!!")



class Person:

    def sound(self):

        print("I'm Quaking like a duck ! ")





def make_it_sound(obj):

    obj.sound()

 

만약 Dog, Cat 여러 클래스가 추가 개발되야 된다고 해도, make it sound 함수로 주요 로직을 수행하면 되기 때문에, 유사한 여러 객체를 처리할 때 유용합니다.

 

저의 경우에는 데이터를 수집하는 ETL 프로그램에서 덕타이핑을 유용하게 쓰고 있습니다. RestAPI 로 수집하거나, DB 로부터 수집하거나, 파일로부터 수집하거나 “수집”이라는 행동은 공통된 작업이기 때문입니다.

 

그러나 덕 타이핑은 종종 런타임 에러를 낼 수 있는 위험도 있습니다. 예를들어 메소드의 이름이이나 로직이 변경될 때 AttributeError 가 발생할 겁니다.

 

“오리처럼 생겼고, 오리처럼 헤엄치고, 오리처럼 꽥꽥 거린다면 아마도 그것은 오리일 것입니다.”

 

덕 타이핑이란 이름은 위 속담에서 유래가 되었다고 합니다. 

 

끝.

반응형
반응형

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

 

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"])

 

 

 

 

 

감사합니다. 

 

 

 

.

 

 

 
반응형
반응형

 

요즘 너무 유튜브 쇼츠에 빠져있는 것 같아, "인간 실격"이라는 책을 읽었다.  출퇴근 시간에 틈틈히 읽었고, 완독 까지 두 달 정도 걸렸다.

 

책의 구조가 독특하다. 서문, 1~3번 째 수기, 후기 이렇게 목차가 구성되있는데 요조라는 남자가 본인의 일생을 일기처럼 기록한 것이 수기 부분이고, 서문과 후기는 요조의 이야기를 전하는 작가의 시점으로 되있다. 후기에서 작가는 요조의 사진과 노트를 받는 내용이 나온다.

결국 이 책은 주인공 요조라는 불행해 보일 수 있는 남자의 인생을 조망한다. 

 

“혼자만 아주 별난 사람인 듯 느껴져 불안과 공포에 바들바들 떨 뿐입니다. 저는 주위사람과 대화를 거의 나누지 못합니다. 그래서 생각해낸 묘안이 광대였습니다.”

 

유복하게 태어났고, 자기가 다른 사람과는 다름을 깨달은 요조가 남들과 섞이기 위해 어쩔 수 없이 광대 노릇을 하는 것이, 회사에서 일도 잘하고 전문적가이기 위해 애써 노력하는 나와 닮았다. 

 

 

“세상이란 개인이 아닐까 하는 흡사 사상같은 것을 터득하게 되었습니다.’”

 

세상은 다수의 인간일까? 세상의 실체가 있는걸까? 사실 세상은 너일 수 있고 , 나일 수 있다. 

 

 

 

그리고 요조는 아버지의 기대에 만족하기 위해 살아가는 것이 싫었어도, 결국 아버지의 경제적 도움 없이는 아무것도 할 수 없다는 것을 안다.  자기모순이다. 

 

 

“남부끄러운 적이 많은 일생이었습니다.”
“알코올 중독일지도 모르겠습니다. 지금도 마시고 싶거든요”
“죽고 싶다. 차라리 죽고싶다. 이제는 돌이킬 수 없다. 거듭 부끄러움을 당할 뿐이다.” 

 

 

 책을 읽는 내내 마음속으로 “술좀 그만 마셔!!”를 속으로 외치던 난 그렇게 폐인이 되어가면서 ‘인간, 실격’의 모습으로 변해가는 요조를 보고 안타까운 한숨을 내쉬고 멈췄다. 나도 그랬었으니까. (20대에  술과 담배, 온갖 게임 중독에 빠져 살았던 시절을 떠올렸다.) 나도 가끔은 일이 제대로 풀리지 않을 때 자기혐오까지는 아니지만 내 자신을 질책한다. 

 

“누군가의 권유를 거부한 적은 제 인생에서 그때 단 한번뿐이었다고 해도 과언이 아닙니다. 저의 불행은 거부하는 능력이 없는 자의 불행이었습니다.”…..”저는 어쩌면 그 순간, 이미 중독에서 벗어난 게 아니었을까요.”

 

이래서 자존감이 중요한걸까? 

그리고 요조가 호리키와의 만남에서도 술, 담배, 매춘부, 좌익 사상 중에 그 어떤 것이라도 거부했다면 지금과 다른 인생을 살지 않았을까? 그런 상상을 해본다.

 

 

마지막으로 인상 깊은 책의 구절도 하나 남기면서 끝낸다. 

신뢰하기 때문에, 사람을 의심하는 법을 몰랐기 때문에 일어난 비극 때문에 요조는 이렇게 얘기한다.

 

신에게 묻습니다. 신뢰는 죄입니까?  
 

 
 
반응형
반응형

 

1.문제 소개

 

 

2.코드

def set_test_case():
    input_str = input().split(" ")
    n,m = int(input_str[0]),int(input_str[1])
    rectangle = []
    for i in range(n):
        row = input()
        rectangle.append(row)

    return n,m,rectangle

def get_max_area(rectangle,x,y):
    distance= 0
    max_area = 0
    while (x+distance) < len(rectangle) and (y+distance) < len(rectangle[0]):
        if (rectangle[x][y] == rectangle[x+distance][y] == rectangle[x][y+distance] == rectangle[x+distance][y+distance]):
            max_area = distance
        distance +=1
    return max_area+1

def solution():
    n,m,rectangle = set_test_case()
    answer = 0
    for i in range(n):
        for j in range(m):
            max_area = get_max_area(rectangle,i,j)
            if max_area > answer:
                answer = max_area

    print(answer*answer)



if __name__ == "__main__":
    solution()
 

 

 

3.코멘트

  • get_max_area(사각형의 p1 / 왼쪽 상단 꼭지점 좌표를 기준으로 p2, p3, p4 가 같은 최대 거리를 반환하는 함수)를 모든 지점을 순회하면서 최대 길이를 구한다.
  • 퇴근하고 짧은 시간에 풀만한 제일 만만한게 그리디/구현 문제.. 오늘은 일이 잘 안풀리는 날이였다.

반응형
반응형

파이썬에서도 객체지향 프로그래밍(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}") 

 

코드가 조금 더 쉬워보여요

 

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

 

끝.

반응형
반응형

pandas란?

  • 데이터 분석을 위한 파이썬 라이브러리
  • 행과 열로 이루어진 데이터 구조를 다룬다.
  • Series 와 Dataframe 자료구조를 사용한다.

 

 

Dataframe 생성

import pandas as pd

#테스트 데이터 dict 생성
data_exam = {
    'name':['a','b','c','d','e'],
    'height':[160,170,180,185,190],
    'age':[23,15,18,19,25],
    'class':['A','B','A','B','A']
}
.
#pandas dataframe 
df=pd.DataFrame(data_exam)

#read from csv file
df=pandas.read_csv(filepath,names=cols,header=None,encoding="cp949")


#Dataframe()으로 호출하면 안되니까 타이핑에 주의하자

df.index
df.columns
df.dtypes

 

Dataframe 다루기

> 특정 컬럼 조회
df['id'] 

> 특정 복수 컬럼 조회.
df[['id','age']] 

> where 절 조건 조회

df[('id'>10)] # ID 10이상만 조회

df.where(filter1 & filter2, inplace = True)


>컬럼 이름 변경
df.rename(columns={'oldName':'newName'},inplace=True)


>Dataframe convert to tuple
tmp = df['id']
mytuple = [x for x in tmp.to_numpy()]

> 컬럭 삭제
df = df.drop(['컬럼이름','col2'],axis=1)
df.dropna(subset=['컬럼이름'],inplace=True)


> 데이터 컬럼 값 변경
df['Gender'].replace('male',1) # Gender 컬럼의 male 값을 1로 대체
df.loc[df['Gender']=='male','Gender']=1



> 특정 컬럼이 널값인 경우 조회
df[df['컬럼1'].isnull()]


> where, query
dt.where(dt.컬럼명1 == 'value').count()['id']

dt.query('컬럼명1 == "value" | 컬럼명2 > 5').count()


> Group By 
dt.groupby('class') #집계할 컬럼
.agg({'height':['count','mean']}) #집계 함수를 적용
.sort_values([('height','mean')]) # 정렬

#agg 에 컬럼 이름을 주면 multiIndex로 생성되는데 () set 형태로 컬럼 이름이 생성된다.


dt.groupby('컬럼이름').size()  그룹별 카운트


> merge

merge_view=view.merge(user,on='uid').merge(product,on='pid')
merge_view=view.merge(user,on='uid').merge(product,on='pid',how='outer')

 

 

Parquet 파일을 Pandas DataFrame 으로 로드하기

rom fastparquet import ParquetFile

# 만약 압축을 풀어야 한다면
import snappy
def snappy_decompress(data, uncompressed_size):
    return snappy.decompress(data)


pf = ParquetFile('sample.snappy.parquet') # filename includes .snappy.parquet extension
df=pf.to_pandas()
pf = ParquetFile('/Users/amore/178309483_20220714_0000_000000000000.parquet')

## 2
df=pd.read_parquet('/Users/amore/appsflyer.snappy.parquet')
df.head()
print(df.head())

 

MySQL 테이블로부터  Pandas DataFrame 으로 로드하기

from sqlalchemy import create_engine
import pandas as pd
import pymysql



db_conn_str = 'mysql+pymysql://User:비밀번호@DB호스트'
mysql_conn = create_engine(db_conn_str)


data = pd.read_sql_table('BOT_USER',mysql_conn)
 
반응형

+ Recent posts