반응형

Intro


안녕하세요, 오늘은 python의 Mixin에 대해서 정리해보고, 제가 이해하는데 도움이 됐던 코드를 소개해드리겠습니다.

Mixin vs. Inheritance (상속)


Mixin은 Inheritance의 한 종류 입니다. 하지만 쓰임에 있어서 일반적인 inheritance와 미묘하게 다른 차이가 있습니다.

간단하게 말하자면 일반적인 inheritance에서는 핵심 기능을 부모 class에, 부가적인 기능을 자식 class에 구현합니다.

예를 들면 부모 class는 Car, 자식 class는 SportsCar, Truck 등이 될 수 있습니다.

다만 mixin은 어떤 핵심 기능을 구현한 class가 부가적인 기능을 추가하기 위해 특정 클래스를 상속 받습니다.

마치 닭과 달걀의 관계와 유사하다고 느껴지실 수 있겠지만, mixin은 다양한 기능이 조합되어야 하는 문자열 처리, 또는 프레임워크를 사용할 때 주로 쓰입니다.

아래 두 가지 예시 코드를 보겠습니다.

Tokenizer with Mixin


주어진 문자열을 -을 기준으로 분리해주는 tokenizer인 BaseTokenizer 클래스가 있습니다. 이를 상속한 Tokenizer 클래스가 있는데, 여기에 각 token을 대문자로 바꿔주는 기능을 추가하고 싶습니다. 즉 주기능은 tokenize인데, 여기에 대문자 변환 기능을 추가하고 싶은 것이죠.

여기서 의문이 생길 수 있습니다. 그냥 Tokenizer 클래스에 대문자 변화 method를 추가해주면 되니까요.

하지만, tokenizer는 tokenizer여야 합니다.

따라서 클래스 안에 부가적인 이것 저것 기능을 구현하기 보다는 필요한 기능이 구현된 클래스를 상속하는게 더 좋은 코드가 되는 것이죠.

아래 예시 코드는 파이썬 클린 코드의 코드를 가져왔습니다.

class BaseTokenizer: 
    def __init__(self, str_token): 
        self.str_token = str_token 
    def __iter__(self): 
        yield from self.str_token.split("-")

class UpperIterableMixin: 
    def __iter__(self): 
        return map(str.upper, super().__iter__()) 

class Tokenizer(UpperIterableMixin, BaseTokenizer): 
    pass

tokens = Tokenizer("28a2320b-fd3f-4627-9792-a2b38e3c46b0")
list(tokens)

>> ['28A2320B', 'FD3F', '4627', '9792', 'A2B38E3C46B0']

이와 같이, UpperIterableMixin을 상속함으로써 클래스에 추가적인 기능을 지원해주는 것을 Mixin이라고 할 수 있습니다.

Database with Mixin

아래 코드는 마이크로소프트에서 제공하는 Mixin 소개 영상의 코드를 가져왔습니다.

우리가 어떤 framework를 사용하려고 합니다.framework는 다양한 기능을 제공하죠, 특히 database에 접근할 때는, database에 접속하고, 관련 log를 남겨야 합니다.

하지만 앞서 말했듯이, 접속과 로깅은 database만의 기능이 아닙니다. database는 데이터만 잘 가져오고, 저장하면 됩니다. 따라서 이러한 기타 기능들을 추가하기 위해서 mixin을 사용할 수 있습니다. 아래 코드의 LoggableConnection이 해당합니다.

Database를 다루는 framework는 이러한 기능이 구현되었는지 확인한 뒤, 기능을 수행합니다.

class Loggable:
    def __init__(self):
        self.title = ''
    def log(self):
        print('Log message from ' + self.title)

class Connection:
    def __init__(self):
        self.server = ''
    def connect(self):
        print('Connecting to database on ' + self.server)

class SqlDatabase(Connection, Loggable):
    def __init__(self):
        self.title = 'Sql Connection Demo'
        self.server = 'Some_Server'

def framework(item):
    if isinstance(item, Connection):
        item.connect()
    if isinstance(item, Loggable):
        item.log()

sql_connection = SqlDatabase()
framework(sql_connection)

>> Connecting to database on Some_Server
>> Log message from Sql Connection Demo

마무리


오늘은 python의 mixin에 대해서 정리했습니다. 글 읽어주셔서 감사합니다!

References


파이썬 클린 코드
Mixin 소개 영상

반응형
반응형

Intro


안녕하세요, 오늘은 decorator에 대한 마지막 정리 글입니다. 앞선 글들을 통해 우리는 decorator란 무엇인지, 어떤 기능을 하는지, 그리고 @property, @staticmethod, @classmethod와 같은 기본 decorator들에 대해서 알게되었습니다.

이번 글은 decorator 정리에 대한 마지막 글로, 제가 프로그래밍하면서 마주쳤던 그 외 decorator인 dataclass와 functools에 대해서 정리하겠습니다.

@dataclass


@dataclass는 class를 선언할 때 class위에 붙여주는 decorator입니다. 이 decorator가 붙은 class는 dataclass가 되고, dataclass의 instance는 아래의 기능들을 수행할 수 있게 됩니다.

@dataclass를 사용하기 위해서는 dataclass 함수를 import 해줘야 합니다.

from dataclass import dataclass
  1. intance를 출력하면 갖고 있는 필드 값 나열 (repr)
  2. hashable해서 dict나 set에 추가 가능
  3. 대소관계 비교
  4. 필드 값 변경 방지

위의 기능을 무조건 구현하는 것은 아니고, @dataclass decorator의 인수를 조절해서 원하는 기능만 추가할 수 있습니다.
인수의 종류 및 기본 값은 아래와 같습니다.

@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)

  • init: __init__이 구현되어 있지 않은 경우에 instance를 생성할 때 받은 값들을 자동으로 instance의 필드 값으로 매핑해주는 __init__함수를 추가해줍니다.
  • repr: __repr__을 추가해줍니다. 이를 추가하면 instance를 print했을 때, instance가 갖고있는 필드 값들을 알 수 있습니다.
  • eq: __eq__를 추가해줍니다. 이를 추가하면 instance가 갖고있는 필드 값들이 모두 동일하면 instance 비교 연산 (==)에서 True를 반환합니다.
  • order: __lt__, __le__, __gt__, __ge__을 추가해줍니다. 이를 추가하면 instance사이의 필드 값에 기반한 대소관계 비교가 가능해집니다.
  • unsafe_hash: __hash__를 추가해줍니다. instance를 hashable하게 만들어줘서 dict나 set의 key로 사용될 수 있습니다.
  • frozen: instance의 필드를 불변하게 만듭니다.

이렇듯, @dataclass는 우리가 원하는 기능을 구현하기 위해서 번거롭게 반복해야 하는 단순한 로직들을, 손쉽게 추가할 수 있도록 돕습니다.

주의) 넘겨주는 인자의 조합, 또는 기존 class에 추가하고자 하는 기능을 담당하는 매직 메소드 (__~~~__)가 이미 추가되어있을 시, 에러가 발생할 수 있습니다. 이에 대해서는 python의 dataclass구현 코드에 적힌 doc string을 참고하셔서 미리 예방하는 것을 추천드립니다.

@dataclass와 관련된 code test는 이미 너무 잘 정리된 블로그 글을 발견해서, 해당 URL을 남기는 것으로 대체하도록 하겠습니다.
[파이썬] 데이터 클래스 사용법 (dataclasses 모듈)

functools


functools는 함수에 여러 기능을 부여해줄 수 있는 모듈입니다. 오늘은 functools가 제공해주는 여러 기능 중 cached, cached_property에 대해서 알아보겠습니다.

@cache decorator는 함수가 이전에 받았던 인수에 대한 결과값을 caching하는 것 입니다. 메모리는 좀 소모되겠지만, 중복 연산을 피할 수 있습니다.

피보나치 함수는 잘못 구현하면 내부에서 무수히 많은 recursive call이 발생해서 결과 값을 얻지 못할 수 있습니다. 그래서 이전에는 중복 연산이 발생하지 않도록 임의로 로직을 구현했지만, @cache를 사용하면 쉽게 해결할 수 있습니다.

아래는 예시입니다.

from functools import cache
import time

@cache
def fibonacci_cached(n: int) -> int:
    if n in [0, 1]:
        return 1
    else:
        return fibonacci_cached(n-1) + fibonacci_cached(n-2)

def fibonacci_uncached(n: int) -> int:
    if n in [0, 1]:
        return 1
    else:
        return fibonacci_uncached(n-1) + fibonacci_uncached(n-2)

num = 40
print(f"start fibonacci{num} w/ cache")
start = time.time()
print(fibonacci_cached(40))
end = time.time()
print(f"result: {end - start} sec\n")

print(f"start fibonacci{num} w/o cache")
start = time.time()
print(fibonacci_uncached(40))
end = time.time()
print(f"result: {end - start} sec")

결과는 아래와 같습니다. @cache가 붙은 fibonacci_cached()의 실행 결과가 훨씬 빠름을 알 수 있습니다.

start fibonacci40 w/ cache
165580141
result: 0.0 sec

start fibonacci40 w/o cache
165580141
result: 48.55391979217529 sec

@cached_property decorator는 @cache와 달리 이전의 결과 값들을 모두 저장하지 않습니다. @cached_property를 통해 caching 되는 것은 @cached_property가 붙은 함수의 이름과 결과값이, instance의 새로운 필드와 필드 값으로 caching됩니다. 그리고 역시 호출할 때 마다 새롭게 실행되지 않고, caching된 값을 반환합니다.

아래는 예시입니다.

from functools import cached_property
import time

import numpy as np

class DataSet:
    def __init__(self, sequence_of_numbers: np.ndarray):
        self._data = tuple(sequence_of_numbers)

    @property
    def stdev_property(self) -> np.float64:
        return np.std(self._data)

    @cached_property
    def stdev_cached_property(self):
        return np.std(self._data)

dataset = DataSet(np.random.rand(1000))

print('Before stdev_property')
print(dataset.__dict__.keys())
print('\nAfter stdev_property')
dataset.stdev_property
print(dataset.__dict__.keys())
print('---------------------------------------')
print('\nBefore stdev_cached_property')
print(dataset.__dict__.keys())
print('\nAfter stdev_cached_property')
dataset.stdev_cached_property
print(dataset.__dict__.keys())

print('---------------------------------------')
print('\nstdev_cached_property value before _data changed')
print(dataset.stdev_cached_property)
dataset._data = np.random.rand(100)
print('\nstdev_cached_property value After _data changed')
print(dataset.stdev_cached_property)

결과는 아래와 같습니다. @cached_property가 붙은 stdev_cached_property()의 실행 결과 instance의 새로운 필드로 추가된고, 이후에 호출하면 재 실행없이 caching된 값이 반환되는 것을 볼 수 있습니다.

Before stdev_property
dict_keys(['_data'])

After stdev_property
dict_keys(['_data'])
---------------------------------------

Before stdev_cached_property
dict_keys(['_data'])

After stdev_cached_property
dict_keys(['_data', 'stdev_cached_property'])
---------------------------------------

stdev_cached_property value before _data changed
0.289598527937108

stdev_cached_property value After _data changed
0.289598527937108

마무리


4개의 글에 거쳐서 decorator에 대해서 정리해봤습니다! 모든 decorator를 다루진 못했지만, decorator의 메카니즘에 대해서 이해할 수 있었던 계기였던 거 같습니다.

감사합니다.

References


Python 3.9 dataclass doc string
Python 3.9 - dataclass documentation
[파이썬] 데이터 클래스 사용법 (dataclasses 모듈)

반응형
반응형

Intro


안녕하세요. 이번 포스팅에서는 C++의 장점 중 하나인 캡슐화에 대해 알아보도록 하겠습니다. 캡슐화는 클래스가 갖고 있는 멤버의 일부를 감춰 클래스를 블랙박스 화하는 것입니다. 즉, 사용자에게 가치가 없는 멤버를 감추는 것입니다. 보통 학부 때 교수님들이 캡슐화에 대해 설명할 때 리모컨에 빚대어 설명을 하십니다. 우리가 채널을 바꿀 때 누르는 버튼은 공개되어 있지만, 실제 버튼을 누르고 TV에 신호 주는 건 캡슐화로 감춰져 있습니다. 즉, 리모컨이 TV로 채널 변경 신호를 보내는걸 굳이 알 필요가 없기 때문입니다. 그렇기 때문에 캡슐화의 목적은 불필요한 것은 캡슐화하여 감추고 프로그래밍을 효율화하는 테크닉입니다.

접근 지정어


접근 지정어 : 클래스를 사용하는 측으로부터 멤버를 이용할 수 있는가의 여부를 결정한다. 접근 지정어에는 public; private; protected 3가지가 있다.

  • public : 지정된 멤버 공개
  • private : 지정된 멤버 비공개. 접근 지정어를 생략했을 때 디폴트는 private.
  • protected : 클래스가 사용되는 방법에 따라서 private: 혹은 public: 어느 쪽인가로 이루어지는 것. 클래스의 객체를 만들어 사용한다면 private:와 같은 의미가 되고 클래스를 상속하여 사용한다면 public:과 동일한 의미가 된다.

UML 클래스 다이어그램에 멤버

  • UML의 클래스 다이어그램에서는 public:는 +, private:는 -, protected:는 #으로 나타낸다.

마무리


접근 지정어가 없는 프로그래밍 언어에서는 프로그램 속에 존재하는 변수나 함수를 프로그램 이외의 부분에서 항상 이용할 수 있게 됩니다. 접근 지정어를 사용하는 장점은 특정한 변수나 멤버 함수 이용을 불가능하게 하는 것에 있습니다. 즉, 크래스의 사용자에게 이용할 가치가 없는 멤버를 감추는 것입니다. 멤버를 감추는 것을 캡슐화라고 합니다.

그럼 이상으로 포스팅을 마치겠습니다. 감사합니다.

반응형
반응형

Intro


안녕하세요. 이번 포스팅에서는 chrono에 대해 알아보도록 하겠습니다. chorono는 C++11에서 추가된 시간에 관련된 라이브러리입니다.
기존의 C 런타임에서 제공하는 time 함수보다 다양한 기능을 제공하며, 사용이 쉽고 정밀도가 훨씬 높습니다. 또한, time함수는 초 단위의 값만 측정할 수 있는데 반해, chrono는 나노 밀리 초 단위까지 측정이 가능합니다. chrono를 사용하면 OS와는 독립적으로 정밀도가 높은 시간 측정이 가능합니다. 또한, 특정 시간 구간에 걸린 시간을 초, 밀리 초, 나노 초 단위로 얻을 수 있으며 시간끼리도 연산이 가능합니다. 예전에 time 함수를 썼다가 시간이 살짝 어긋난 경험이 있어서... 저 같은 경우 시간 관련 코드를 짤 때 chrono를 사용합니다. chrono를 사용하면 OS와는 독립적으로 정밀도가 높은

chrono


#include <iostream>
#include <chrono>
#include <cmath>
int main(void)
{

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    int sum = 0;
    for (int i = 0;i < 999999999;i++)
        sum += i;
    std::chrono::duration<double>sec = std::chrono::system_clock::now() - start;
    std::cout << "for문을 돌리는데 걸리는 시간(초) : " << sec.count() <<"seconds"<< std::endl;



}

결과

여기서 time_point는 시간상의 한 축을 뜻합니다. for문을 실행하여 다시 현재 시각을 얻은 후에 for문을 시작하기 전 저장한 현재 시각을 빼면 for문을 수행하는 데 걸린 시간을 얻을 수 있습니다.

chrono에서 경과 시 간을 나타낼 때 duration 클래스를 사용하는데, duration은 6가지 시간 단위를 지원합니다.
duration 지원 시간 단위

  • std::chrono::nanoseconds : 나노세컨드. 10억 분의 1초
  • std::chrono::microseconds : 마이크로 세컨드. 100만 분의 1초
  • std::chrono::milliseconds : 밀리 세컨드. 1천 분의 1초
  • std::chrono::seconds : 초
  • std::chrono::minutes : 분
  • std::chrono::hours : 시

clock 클래스
chrono에는 system_clock뿐만 아니라 steady_clock와 high_resolution_clock도 있습니다. system_clock이 가장 일반적으로 사용하는 것으로, 시스템 시간을 나타내고 C 런타임의 time_t와 호환이 가능합니다.
steady_clock은 물리적 시간처럼 결코 역행하지 않는 시간을 나타냅니다. 즉, steady_clock 이외의 clock 클래스는 time_point를 얻은 후 OS에서 날짜를 과거로 되돌린 후에 time_point를 얻으면 앞에 얻은 시간을 얻지만, steady_clock에서는 그럴 수 없습니다. 따라서 시간의 흐름이 바뀌지 않는 시간을 얻고 싶을 때 steady_clock를 사용하면 됩니다.
high_resolution_clock은 Windows 또는 Linux에서 제공하는 정밀도가 가장 높은 시간입니다. 이 클래스는 system_clock이나 steady_clock의 다른 별칭으로 정의되기도 합니다.

chrono를 이용해 timer 만들기

#include <iostream>
#include <chrono>
#include <cmath>
int main(void)
{

    auto startTime = std::chrono::system_clock::now();
    int Sec = 0;
    while (1)
    {
        auto endTime = std::chrono::system_clock::now();
        auto sec = std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime);
        if (sec.count() > Sec)
        {
            Sec++;
            std::cout << Sec <<" ";
        }
    }

}

결과

while문을 무한으로 돌려서 콘솔이 시작될 때 무한으로 초를 제는 카운터를 만들어 봤습니다.

마무리


오늘은 시간 관련 라이브러리 중 하나인 chrono에 대해 포스팅해봤습니다. 예전에 개발 중에 기존의 C 런타임 time 함수를 썼는데.. 시간이 미묘하게 맞지 않아서 고생한 적이 있습니다. 그때 chrono의 time함수를 쓰니 정확도가 훨씬 높았습니다. 그래서 C++에서는 웬만하면 시간 관련 함수는 chrono를 사용합니다.

그럼 이상으로 포스팅을 마치겠습니다. 감사합니다.

반응형

'[C++ STL]' 카테고리의 다른 글

[C++ - vector Tuple sort 방법]  (0) 2021.11.16
[C++ For문에서 Vector erase 사용법  (0) 2021.11.01
[C++ STL - forward_list]  (0) 2021.06.24
[STL - 메모리 관리(스마트 포인터 shared_ptr, unique_ptr]  (0) 2021.06.08
[C++ STL - tuple]  (0) 2021.05.21
반응형

Intro


안녕하세요, 오늘은 우리가 python에서 자주 쓰이는 decorator인 @classmethod@staticmethod 대해서 정리해보려고 합니다.

지난 글(LINK)에서 decorator @뒤에는 decorator를 붙인 함수실행된 뒤 실행할 함수의 이름이 온다고 했습니다.

네, classmethodstaticmethod역시 함수 이름입니다. 이들은 각각 앞서 실행된 함수를 class methodstatic method로 변환해주는 함수입니다. class method와 static method는 class나 instance를 통해서 접근할 수 있습니다.

참고) 두 decorator 모두 class내에서 선언되어 사용됩니다. 일발적으로 우리가 class안에 선언해서 첫 번째 인자로 self를 받는 method를 instance method라고 합니다.

instance method, class method, static method


intance method는 우리가 일반적으로 class 내부에 정의하는 self라는 인수를 첫 번째로 받는 method입니다. 이때 우리는 class의 instance를 생성해야만 instance method를 실행할 수 있습니다.

반면에 class methodstatic method는 객체가 없어도 실행될 수 있습니다. 그렇다면 둘의 차이점은 무엇일까요?

그것은 바로 class method는 첫 번째 인자로 항상 class를 받고(일반적으로 cls라고 이름 붙힙니다.), static method는 아무런 인자나 자유롭게 받을 수 있다는 겁니다.

이는 class method는 class내에서 _공유되는 variable, class method, 그리고 static method_에 접근할 수 있고, static method는 다른 variable나 method에는 접근할 수 없고, 인자로 받은 값만으로 할 수 있는 기능을 수행합니다.

따라서 class의 instance들끼리 공유하는 variable이나 class/ static method를 다루는 method를 정의할 때는 @classmethod를 붙여줍니다.

static method는 사실 class내에 꼭 있을 필요는 없지만, class와 연관성이 있는 method를 안에 정의하고 class, instance를 통해 사용함으로써 코드 가독성을 높일 수 있고, 이 경우 @staticmethod를 붙여줍니다.

Code test


간단한 예시를 통해서 @classmethod, @staticmethod decorator의 쓰임을 확인해보겠습니다.
오직 기능 확인만을 목적으로 만든 허접한 예시입니다.

class Employee():
    company = "저승네트워크"

    def __init__(self, name, enter_year):
        self.name = name
        self.enter_year = enter_year

    def self_introduction(self):
        print(f'안녕하세요, 저는 {self.company}에 {self.enter_year}년에 입사한 {self.name} 이라고 합니다.')

    @classmethod
    def company_introduction(cls):
        print(f'저는 {cls.company}에 다닙니다.')

    @classmethod
    def recommendation_with_company(cls, friend):
        cls.recommendation(friend)
        print(f'{cls.company}에 꼭 필요한 사람입니다.')

    @staticmethod
    def recommendation(friend):
        print(f'제 친구 {friend}를 우리 회사에 추천합니다.')

class variable company, instance variable name, enter_year, instance method __init__(), self_introduction(), classmethod company_introduction(), recommendation_with_company(), static_method recommendation을 선언했습니다.

이들을, class 또는 instance를 통해서 사용해보겠습니다.

Employee를 통해 접근할 수 있는 것은 class variable, class method, static method입니다. 그 외에는 instance가 있어야합니다.

instance를 생성한 뒤에는 어느 것이든 접근할 수 있습니다. 애초에 instance들 끼리 서로 공유하라고 class variable/method와 static method가 있는 것이니까요.

마무리


오늘은 python의 @classmethod, @staticmethod decorator에 대해서 살펴봤습니다. 좋은 하루 보내세요!

References


파이썬 - OOP Part 4. 클래스 메소드와 스태틱 메소드 (Class Method and Static Method) (LINK)

반응형
반응형

Intro


안녕하세요. STL에서 기존에 리스트 자료구조를 사용하는 std::list가 있습니다. std::list가 일반적으로는 사용하기 편하지만, 양방향으로 데이터를 탐색할 필요가 없을 때에는 메모리 사용이나 처리 속도 면에서 아쉬운 점이 있습니다. 실제로 개발을 하다 보면 양방향 리스트가 필요한 경우보다는 단방향 리스트 만으로 충분한 경우가 많습니다. 그래서 이번 포스팅에서는 forward_list의 사용방법에 대해 다뤄보도록 하겠습니다.

forward_list


forward_list의 설계 방침

  • 특별한 이유가 없으면 forward_list는 기존 list 설계에 맞춘다.
  • 설계상 선택지가 여러 개이면 성능을 최우선으로 한다.
  • std::list의 insert와 erase를 forward_list에서 제공할 수 있지만, 구현이 복잡해지고 성능 면에서 좋지 않으므로 제공하지 않는다.
  • 다른 stl의 컨테이너에 있는 size 함수를 제공하지 않는다. 요소 수를 보존하는 멤버를 가지고 있으면 C 언어에서 구현한 것과 비교해서 불필요한 메모리를 사용하기 때문이다. forward_list와 list의 차이점 forward_list와 list의 차이점

STL의 list 컨테이너와 다른 점

  • forward_list는 단방향 리스트다. 각 요소는 기 다음 요소를 가리키는 포인터를 하나만 가지고 있다.(list는 양방향 리스트)
  • list에 비해서 메모리를 작게 사용한다. 각 요소의 메모리만이 아닌 컨테이너 그 자체의 사이즈도 작다. int형에 대해서 list가 12바이트라면 forwar_list는 8바이트이다.(32바이트 기준이고, 64바이트에서 list는 24바이트고 forward_list는 16바이트다.)
  • list보다 삽입/삭제 속도가 더 빠르지만, 차이는 크지 않다.
  • 한 방향으로만 이동할 수 있다.
  • 삽입과 삭제는 지정한 요소의 다음 요소에 한해서만 가능하다.
  • forward_list 사용하기forward_list 사용하기

forward_list 사용하기
list컨테이너와 forward_list는 크게 차이가 없습니다. 다만, forward_list는 단방향 리스트라는 것과 다른 컨테이너에서 지원하는 일부 기능이 없습니다. 그 점을 유의하셔야 합니다.

#include <iostream>
#include<forward_list>
using namespace std;
int main(void)
{

    forward_list<int> flist;
    forward_list<int> flist2;

    for (int i = 0;i < 5;i++)//0~4까지 forward_list에 입력
        flist.push_front(i);
    for (auto value : flist)
        cout << value << endl;

    flist2.assign(flist.begin(), flist.end());
    for (auto value : flist2)
        cout << value << endl;


}

결과

데이터 추가하기

  • insert_after : 저장된 위치 뒤에 새로운 데이터를 추가한다.
  • emplace_after : 지정된 위치 뒤에 새로운 데이터를 추가한다.
  • emplace_front : 맨 앞에 새로운 요소를 추가한다.
#include <iostream> 
#include<forward_list> 
using namespace std; i
nt main(void) 
{ 
    forward_list <int> flist; 
    forward_list <int> flist2; 
    forward_list <int> flist3; 
    for (int i = 0;i < 5;i++) 
    { 
        flist.push_front(i); 
        flist2.push_front(i); 
        flist3.push_front(i);
    }
    cout << "insert_after를 사용하여 추가" << endl;
    flist.insert_after(flist.begin(), 99);
    for (auto value : flist)
           cout << value << " ";
    cout << endl;
    cout << "emplace_after를 사용하여 추가" << endl;
    flist2.emplace_after(flist2.begin(), 99);
    for (auto value : flist2)
        cout << value << " ";;
    cout << endl;
    cout << "emplace_front를 사용하여 추가" << endl;
    flist3.emplace_front(99);
    for (auto value : flist3)
        cout << value << " ";
}

결과

데이터 삭제하기

  • pop_front : 첫 번째 위치의 데이터를 지운다.
  • rease_after : 지정된 위치 다음이나 지정된 위치 이후의 지정한 범위에 있는 모든 데이터를 지운다.

#include  
#include  
using namespace std;  
int main(void)  
{


forward_list<int> flist;
forward_list<int> flist2;
forward_list<int> flist3;

for (int i = 0;i < 5;i++)
{
    flist.push_front(i);
    flist2.push_front(i);
    flist3.push_front(i);
}

cout << "pop_font로 삭제" << endl;
flist.pop_front();
for (auto value : flist)
    cout << value << " ";;

cout << "erase_after로 삭제" << endl;
flist2.erase_after(flist2.begin());
for (auto value : flist2)
    cout << value << " ";;

cout << "erase_after로 범위를 지정하여 삭제" << endl;
flist3.erase_after(flist3.begin(), flist3.end());
for (auto value : flist3)
    cout << value << " ";;

}

결과

마무리


오늘은 forward_list에대해 포스팅했습니다. 이번 포스팅에서는 sort나 unique를 이용한 중복 제거, merge는 들어가지 않았습니다. 참조 부탁드립니다.
사실 forward_list는 list보다 성능면에서 이점이 있지만, 사용자가 사용하기에는 list가 편하긴 합니다.
그럼 이상으로 포스팅 마무리하겠습니다. 감사합니다.

반응형
반응형

Intro


안녕하세요, 지난글에서는 python의 decorator에 대해서 간단히 살펴봤습니다.
이번 글에서는 python에서 기본으로 제공해주면서 많이 쓰이는 @property decorator에 대해서 정리하겠습니다.

@property


@property decorator는 많은 분들이 private 변수의 getter, setter를 설정하기 위해서 많이 사용한다고 해서, 그 용도로만 쓰이는 건줄 알았습니다. 하지만 조금 더 알아본 결과, 제가 생각하는 @property decorator의 본질은 "function(함수)을 variable(변수)처럼 사용하기" 입니다.

예를 들면 아래와 같습니다.

class PropertyTest():
    @property
    def func_with_property(self):
        print("called with property")

    def func_without_property(self):
        print("called without property")

if __name__ == "__main__":
    pt = PropertyTest()

    tc.func_with_property
    ## output: called with property

    tc.func_without_property
    ## output: <bound method PropertyTest.func_without_property of <__main__.PropertyTestClass object at 0x0000016FC1D827F0>>

    tc.tc.func_without_property()
    ## output: called without property

위의 예시를 보시면, @property 를 위에 적은 func_with_property를 호출할 때는 ()를 안붙여도 그 반환값을 알 수 있습니다.
이러한 특성을 활용해서 private 변수의 getter, setter를 설정할 수 있는 겁니다.

우선 python에서도 private 변수를 설정할 수 있는 걸 모르는 분이 있을 수 있는데요, 변수이름 앞에 __ 또는 _를 붙여서 외부로부터 접근을 제어할 수 있습니다. 그래서 private 변수에 접근하기 위한 getter와 setter가 필요한 것이죠.

python의 private 변수와 관련한 내용은 따로 다루도록 하겠습니다.

먼저 일반적인 getter, setter를 이용한 private 변수와 public 변수로의 접근을 비교해보겠습니다.

class GetterSetterWithFunc():
    def __init__(self, pri_var, pub_var):
        self.__pri_var = pri_var
        self.pub_var = pub_var

    def get_pri_var(self):
        return self.__pri_var

    def set_pri_var(self, pri_var):
        self.__pri_var = pri_var

if __name__ == "__main__":
    gswf = GetterSetterWithFunc(1, 10)

    gswf.__pri_var
    ## output: AttributeError

    gswf.pri_var
    ## output: AttributeError

    gswf.get_pri_var()
    ## output: 1

    gswf.set_pri_var(100)
    gswf.get_pri_var()
    ## output: 100

    gswf.pub_var
    ## output: 10

    gswf.pub_var = 1000
    gswf.pub_var
    ## output: 1000

위의 예시와 같이, private 변수인 pri_varget_pri_var()set_pri_var()을 통해서만 접근이 가능한 반면, public 변수인 pub_var은 바로 접근이 가능합니다.

그런데, private 변수와 public 변수를 다룰 때 이를 구분해서 사용하기 불편하기도 하죠, @property decorator를 사용하면 private, public상관없이 다룰 수 있습니다. 어떻게 달라지는지 보겠습니다.

class GetterSetterWithProperty():
    def __init__(self, pri_var, pub_var):
        self.pri_var = pri_var
        self.pub_var = pub_var

    @property
    def pri_var(self):
        return self.__pri_var

    @pri_var.setter
    def pri_var(self, pri_var):
        self.__pri_var = pri_var

if __name__ == "__main__":
    gswp = GetterSetterWithProperty(1, 10)

    gswp.pri_var
    ## output: 1

    gswp.pri_var = 100
    gswp.pri_var
    ## output: 100

    gswp.pub_var
    ## output: 10

    gswp.pub_var = 1000
    gswp.pub_var
    ## output: 1000

이와 같이, @property를 사용하면 private, public 변수를 같은 방법으로 다룰 수 있게 됩니다. 다만 차이점이 있다면, __init__()에서 호출되는 self.pri_var는 변수가 아닌 아래에 정의된 pri_var function이고, private 변수인 pri_var는 해당 function을 실행할 때 생성 됩니다. 반면 pub_var__init__()에서 바로 생성됩니다.

마무리


오늘은 python의 기본 decorator 중 하나인 @property decorator에 대해서 정리해봤습니다. 다음 글에서는 @staticmethod, @classmethod에 대해서 정리하겠습니다.

글 읽어주셔서 감사합니다!

반응형
반응형

Intro


안녕하세요, 오늘은 우리가 python코드를 보다보면 발견할 수 있는 decorator에 대해서 정리해보려고 합니다.
decorator는 class나 method를 정의할 때, 그 위에 @로 시작하는 구문을 얘기합니다.

Java의 annotation vs. Python의 decorator


Java언어에도 @을 사용하는 annotation 이 있죠.

하지만, 두 단어의 이름이 다르듯이, 의미또한 다릅니다.참고

Java의 _annotation_은 @을 달아주는 객체에 대한 추가적인 meta data를 제공해주는 역할을 합니다. 이것 자체가 어떤 로직으로서의 역할을 한다기 보다는, 컴파일러가 이 정보를 참고해서 코드를 실행하는데 도움을 주는 역할로 보면 될 거 같습니다. (잘 활용하면 로직으로서도 사용이 가능하다는 거 같은데... 정확히는 모르겠습니다.)

반면에 Python의 decorator는 로직으로서의 역할을 합니다. 즉, 정의 위에 decorator가 붙은 함수를 실행하면, 그 함수 이외에 실행되는 로직이 있고, 그 로직을 수행할 함수의 이름을 @뒤에 써주는 겁니다.

Code test


간단한 예시를 통해서 decorator의 쓰임을 확인해보겠습니다.
default_path에 위치한 config.json 파일의 전체 filepath를 반환해주는 함수 get_config_fp()이 있다고 합시다.
그런데 처음에 이 함수를 짤 때, 실수로 인자로 받은 default_path를 그대로 반환하게 실수했다고 합니다. (또는 기존의 함수에 어떤 기능을 추가하고 싶다고 합시다.)

def get_config_fp(default_path):
    return default_path

decorator를 사용하면, 원래 함수의 수정없이 원하는 기능을 추가해줄 수 있습니다. decorator의 활용법을 두루 익히기 위해서, 불필요하지만 아래 2개의 decorator역할을 해줄 함수를 정의했습니다.

## default_path뒤에 "/"를 추가해주는 decorator
def add_slash(func):
    def slash_decorator(default_path):
        return f"{func(default_path)}/"
    return slash_decorator

## default_path뒤에 파일 이름을 추가해주는 decorator
def add_fn(filename):
    def fn_decorator(func):
        def func_wrapper(default_path):
            return f"{func(default_path)}{filename}"
        return func_wrapper
    return fn_decorator

add_slash는 기능을 추가하고자 하는 함수 외에 다른 인자를 받지않는 decorator이고, add_fn는 다른 인자도 함께 받는 decorator입니다.
위의 구현에서도 보이듯이, 추가 인자를 받지 않는 add_slash는 func -> func의 인자 순으로 입력을 받고, add_fn은 추가 인자 -> func -> func의 인자 순으로 입력을 받아 처리하는 것을 알 수 있습니다.

decorator의 동작을 조금더 자세히 보기 위해서, get_config_fp()와 같은 기능을 하는 함수들을 아래와 같이 정의했습니다.

def get_config_fp(default_path):
    return default_path

@add_slash
def get_config_fp2(default_path):
    return default_path

@add_fn('config.json')
@add_slash
def get_config_fp3(default_path):
    return default_path

보시는 바와 같이 decorator는 여러 개를 사용할 수 있습니다. 실행되는 순서는 target fucntion과 가까운게 먼저 실행됩니다.

전체 코드 및 수행 결과는 아래와 같습니다.

마무리


오늘은 python의 decorator에 대해서 간단히 살펴봤습니다.
다음에는 python에서 많이 사용되는 주요 decorator들과 그 사용법에 대해서 정리하겠습니다.
감사합니다. 좋은 하루 보내세요!

References


PEP 318 -- Decorators for Functions and Methods(LINK)

반응형
반응형

Intro


안녕하세요. 이번 포스팅에서는 C++ STL에서 메모리 관리를 위해 만들어진 shared_ptr과 unique_ptr에 대한 정리를 하도록 하겠습니다. C/C++는 동적으로 할당한 메모리를 사용한 후 해제하지 않으면 메모리 누수가 발생합니다. 보통 개발하다 보면 malloc나 new로 메모리를 할당하고 제대로 해제하지 않아 메모리 누수가 자주 자오죠 ㅎㅎ. 저도 이런 실수를 자주 합니다. 실수 없이 할당된 메모리를 완벽하게 해제해주면 상관없지만 대형 프로젝트에서는 필연적으로 발생합니다.

그래서 Java나 C#을 C++과 비교할 때 메모리 관리에 대한 차이점이 나옵니다. Java나 C#은 메모리를 자동으로 관리합니다. 프로그래머가 메모리를 할당하면 자동으로 해제를 해주기 때문에 프로그래머 입장에서는 편리합니다. 하지만 보통 메모리를 자동으로 관리하는게 어떻게 보면 편하고 좋을 수도 있지만, 꼭 좋은 것만은 아닙니다. Java나 C#은 성능에 문제가 된다면 가차 없이 메모리를 해제하여 가비지 컬렉션이 발생합니다. 가비지 컬렉션이 자주 발생하는 건 리소스를 많이 소비하기 때문에 너무 자주 작동하면 성능상 좋지 않습니다. 하지만 메모리를 자동으로 관리해주는 것은 분명 편리한 기능입니다. 그래서 C++11에서 STL 라이브러리로 메모리를 자동으로 해제시켜 주는 shred_ptr와 unique_ptr이라는 라이브러리가 생겼습니다. 그리고 동적으로 할당한 메모리 해제를 자동으로 관리해주는 것을 스마트 포인터라고 합니다.

shared_ptr


많은 객체나 데이터를 다루다 보면 배열식으로 동적 할당을 해야 할 때가 자주 있습니다. shared_ptr도 배열로 할당이 가능합니다. 간단하게 기존 C++의 new와 shared_ptr을 사용하여 int 배열을 만들어 비교해 보도록 하겠습니다.

#include <iostream>
#include <memory>
using namespace std;
int main(void)
{
    shared_ptr<int>shared_ptr_array_test(new int(10));
    int* array_test = new int[10];

    for (auto i = 0;i < 10;i++)
    {
        shared_ptr_array_test.get()[i] = i;
        array_test[i] = i;
    }
    cout << "shared_ptr출력" << endl;
    for (auto i = 0;i < 10;i++)
        cout <<shared_ptr_array_test.get()[i] << endl;

    cout << "일반 동적할당 출력" << endl;
    for (auto i = 0;i < 10;i++)
        cout << array_test[i] << endl;

    delete[] array_test;
}

결과

결과를 보면 new와 shared_ptr의 결과 값이 같습니다. 하지만 가장 큰 차이는 new는 필연적으로 delete를 해줘야 하지만 shared_ptr은 자동으로 메모리 해제가 됩니다.

unique_ptr


unique_ptr도 앞에서 설명한 shared_ptr처럼 동적으로 할당된 객체를 관리하는 스마트 포인터 입니다. shared_ptr과 다른 점은 이름에 나타나듯이 객체를 독점적으로 관리한다는 것입니다. 사실 이 말이 처음에는 이해가 가지 않았습니다. 구글에 검색해 봤을 때 unique_ptr은 "unique_ptr를 사용하면 동적으로 힙 영역에 할당된 인스턴스에 대한 소유권을 얻고 unique_ptr 객체가 사라지면 해당 인스턴스를 해제하게 됩니다"라고 설명되어 있습니다. 이 의미를 곰곰이 생각해보니 unique_ptr로 선언된 객체는 누구에게도 공유되지 않고, 스코프에서 벗어날 때 메모리에서 자동으로 삭제가 됩니다. 또한 복사 생성자와 할당 연산자가 구현되어있지 않기 때문에 복사를 할 수 없고, 이동만 가능합니다. 사용하는 방법은 shared_ptr과 비슷하기 때문에 코드는 넘어가도록 하겠습니다.

그렇다면 unique_ptr은 어디에 사용하는게 좋을까요..? 그건 바로 클래스의 멤버 변수로 활용할 때 유용합니다. 소멸자가 호출될 때 알아서 해제되기 때문에 편리합니다.

마무리


이번 포스팅에서는 C++ STL의 스마트 포인터에 대해 다뤄봤습니다. 하지만 포스팅 내용은 많이 허접합니다. 그냥 기본적인 스마트포인터에 대한 개념을 정리한 거라고 보시면 될 것 같습니다. 배열 객체를 다루는 것만 봤는데 실제로 다른 객체를 다루면.. 기본적으로 코드가 길어지고 귀찮아서.. 이 정도만 했습니다. 스마트 포인터에서 객체의 포인터를 얻을 때는 get()을 사용하고, 참조를 얻을 때는 opereator(->)을 사용합니다. 그리고 reset() 함수를 사용하면 기존에 관리하던 객체를 다른 객체로 교체가 가능합니다. 이런 것들을 다뤘어야 했는데... 죄송합니다.

스마트 포인터를 자주 사용하면 분명이 이전보다 메모리 관리가 쉬워집니다. 하지만 생각 없이 사용하다 보면 큰 오류를 범할 수 있습니다. 큰 오류의 원인 중 하나가 순환 참조입니다. 순환 참조는 java나 C#에서도 자주 발생하기 때문에 주의해야 합니다. 순환 참조는 단순하기 때문에 이런 실수는 좀처럼 하지 않겠지만, 대형 프로젝트에서는 주의하지 않으면 충분히 발생할 수 있는 문제이기 때문에 주의가 필요합니다.

그럼 이상으로 포스팅을 마치겠습니다. 감사합니다.

반응형

'[C++ STL]' 카테고리의 다른 글

[C++ STL - chrono(시간 측정)]  (0) 2021.06.30
[C++ STL - forward_list]  (0) 2021.06.24
[C++ STL - tuple]  (0) 2021.05.21
[C++ STL - range based for]  (0) 2021.05.13
[C++ STL - 람다(lambda)]  (0) 2021.05.04
반응형

Intro


이번 포스팅에서는 개발자들이 개발한 코드를 문서화할 때 사용하는 Doxygen이란 걸 소개하도록 하겠습니다.
오픈소스 기반 프로젝트를 개발하거나 SDK 별도로 받아 개발할 때 레퍼런스(개발자 문서)를 참조해서 개발해본 경험이 있으실 겁니다.
Doxygen은 개발자들이 개발한 코드에 주석을 작성하면 주석이 달린 코드를 읽어 자동으로 문서를 만들어내는 소프트웨어 레퍼런스 문서 생성기입니다. 저 같은 경우 주로 SDK를 외부에 배포할 때 자주 사용하는데 정말 편리하고 좋습니다. 이번 포스팅에서 doxgen에 관한 전반적인 소개와 Doxygen 다운로드, 설치, 사용법, 주석 방법, 실제 배포할 때 html 파일을 chm파일로 묶어서 배포하는 것 까지 포스팅하겠습니다. 포스팅 내용이 길어질 거 같아서 2번에 나눠서 포스팅하도록 하겠습니다.

Doxygen


Doxygen이란?

주석이 달린 코드를 읽어내 문서를 만드는 소프트웨어 레퍼런스 문서 생성기입니다. 문서의 결과는 보통 HTML로 나오거나, 설정하는 환경에 따라 Word 파일, PDF 파일로 변환하여 만들 수 있습니다. 코드 내의 정해진 규칙에 짜여진 주석으로부터 정보를 읽어오며, 규칙만 지키면 모든 언어의 정보를 읽어 올 수 있습니다.

왜 Doxygen을 사용하나?

Doxygen 양식에 맞는 정해진 주석을 공통적으로 사용하면 다른 사람이 소스코드를 이해하기 편리합니다. 또한 보통 소스 코드 작성 후 별도로 문서를 작성하는데 매우 번거롭습니다. 하지만 Doxygen을 사용하면 문서와 코드의 동기화가 가능하여 빠르고 효율적으로 문서 관리가 가능합니다.

Doxygen 다운로드 및 설치 방법

  1. 다운로드. Windows 버전을 다운로드 해줍니다.
    Doxygen다운
  2. 설치. Next를 눌러주며 설치해줍니다.




Doxygen 사용법

실행 파일 클릭(Doxywizard.exe)
Project 설정

  1. 프로젝트 루트 폴더 지정
  2. 프로젝트 이름 설정
  3. 프로젝트 개요 설정
  4. 프로젝트 버전 입력
  5. 문서 로고 선택
  6. 소스 코드 파일 위치 설정
  7. 하위 폴더의 모든 코드를 문서화할지 여부
  8. Doxygen 문서 생성 폴더
  9. 다음 step

Mode 설정

  1. 문서 or 문서+소스코드 선택
  2. 프로그래밍 언어 선택
  3. 다음 step

Output설정

  1. 포맷 설정
  2. 생성 파일 설정
  3. 다음 setp

Diagrams 설정

  1. Diagram 설정
  2. 다음 step

Run 설정

  1. 문서 생성
  2. 설정 보기
  3. HTML 파일 미리 보기

마무리


이번 포스팅은 내용이 길어져서 .. 2번으로 나누어 포스팅 하겠습니다.이상으로 포스팅을 마치겠습니다. 다음 포스팅에서는 실제 배포할 때 html 파일을 chm파일로 묶어서 배포하는 것 까지 포스팅하겠습니다.

반응형

+ Recent posts