반응형

** 이 기능에 대해 '데코레이터'라는 명칭을 선택한 것에 대해 불만이 많았다. 그중 GoF 책에서 사용하는 용어와 일치하지 않는다는 불만이 가장 컸다. 데코레이터라는 명칭은 구문 트리를 파싱하고 애너테이션하는 컴파일러 분야에서의 용법과 관련이 더 깊다. _ PEP 318 - '함수 및 메서드 데코레이터'

 

특징  Python 데코레이터  GoF 데코레이터
주체 함수 또는 메서드 객체
목적 함수의 동작 변경/확장 객체의 동작 변경/확장
사용 방법 함수 위에 @데코레이터 사용 객체를 감싸는 데코레이터 객체 생성

 

 

Python 데코레이터는 하나의 함수(또는 메서드)를 다른 함수로 감싸서 추가 기능을 제공하는 도구입니다. 데코레이터는 함수의 동작을 수정하거나 확장할 때 유용합니다. 기본적인 형태는 다음과 같습니다:

  1. 기본적인 데코레이터 구조:
    • 데코레이터 함수는 다른 함수를 인자로 받아서 새로운 함수를 반환합니다.
  2. 사용법:
    • 데코레이터를 적용하려는 함수 위에 @데코레이터_이름을 붙입니다.
  3. 응용 사례:
    1. 로그 기록: 함수 호출과 결과를 기록하여 디버깅과 모니터링에 활용.
    2. 실행 시간 측정: 함수의 성능을 분석하고 최적화.
    3. 인증 및 권한 부여: 함수 호출 전에 인증 및 권한 확인.
    4. 캐싱: 함수의 결과를 캐시하여 성능 향상.

 

결국 데코레이터는 편리 구문(syntatic sugar)일 뿐이며 일반적인 콜러블과 동일하게 작동하지만 메타 프로그래밍을 할 때 편리합니다.

#1
@decorate
def target():
	print('running target()')
    
#2
def target():
	print('running target()')
target = decorate(target)

 

#1 와 #2는 본질적으로 같습니다.

 

하지만 #2의 방식은 아래 같은 단점이 있습니다.

  • 가독성 저하: 함수 정의와 데코레이터 적용이 분리되어 코드가 장황해짐.
  • 유지보수성 저하: 함수를 추가하거나 변경할 때 데코레이터 적용 부분도 수정해야 함.
  • 실수 가능성 증가: 데코레이터 적용을 잊어버릴 가능성이 있음.
  • 코드 중복 증가: 동일한 패턴의 반복으로 코드가 지저분해짐.
  • 코드 일관성 문제: 함수 정의와 데코레이터 적용 방식이 일관되지 않음.

 

예제) 1. 로그 기록 데코레이터

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} is called with arguments {args} and {kwargs}")
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} returned {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(3, 5)
Function add is called with arguments (3, 5) and {}
Function add returned 8

 

예제) 2. 실행 시간 측정 데코레이터

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to complete")
        return result
    return wrapper

@timer_decorator
def long_running_function():
    time.sleep(2)
    print("Function complete")

long_running_function()
Function complete
Function long_running_function took 2.0021233558654785 seconds to complete

 

 

데코레이터의 캐시 기능을 사용한 예제입니다.

import time

# 시간 차이를 계산하는 데코레이터 정의
def time_since_start(func):
    start_time = time.time()  # 데코레이터가 정의될 때 시작 시간을 기록

    def wrapper(*args, **kwargs):
        current_time = time.time()
        elapsed_time = current_time - start_time
        print(f"Time since start: {elapsed_time:.2f} seconds")
        return func(*args, **kwargs)
    
    return wrapper

# 데코레이터를 사용하여 함수 정의
@time_since_start
def example_function():
    print("Example function is called")

# 함수 호출
example_function()
time.sleep(2)
example_function()
time.sleep(3)
example_function()

 

출력 결과 

Time since start: 0.00 seconds
Example function is called
Time since start: 2.00 seconds
Example function is called
Time since start: 5.00 seconds
Example function is called

 

임포트 타임에서 time_since_start 함수가 호출될 때 start_time 변수가 선언되고 이후에 데코레이터에서는 wrapper 함수가 호출되면서 처음 선언된 start_time과의 시간 차이가 리턴 값으로 얻어집니다.

 

실제 사용 예제입니다.

 

django에서는 @login_required 라는 데코레이터로 api 요청이 왔을 때 사용자 인증을 거치고,login 상태가 아니면 login 페이지로 리다이렉트 하는 데코레이터를 사용합니다. 

login_required는 사전에 정의된 함수이며 코드는 아래와 같습니다.

# django/contrib/auth/decorators.py

from functools import wraps
from django.http import HttpResponseRedirect
from django.utils.decorators import available_attrs
from django.conf import settings

def user_passes_test(test_func, login_url=None, redirect_field_name='next'):
    """
    사용자 정의 테스트 함수를 통과하면 접근을 허용하는 데코레이터를 반환합니다.
    
    Args:
        test_func: 사용자 테스트 함수. 사용자 객체를 받아서 Boolean을 반환해야 합니다.
        login_url: 로그인 페이지 URL. 기본값은 settings.LOGIN_URL입니다.
        redirect_field_name: 리다이렉트 필드 이름. 기본값은 'next'입니다.
        
    Returns:
        view_func를 감싸는 데코레이터 함수.
    """
    if not login_url:
        login_url = settings.LOGIN_URL

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            # 사용자 정의 테스트 함수로 사용자를 검사합니다.
            if test_func(request.user):
                return view_func(request, *args, **kwargs)
            # 테스트를 통과하지 못하면 로그인 페이지로 리다이렉트합니다.
            path = request.build_absolute_uri()
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(path, login_url, redirect_field_name)
        return _wrapped_view
    return decorator

def login_required(function=None, redirect_field_name='next', login_url=None):
    """
    로그인된 사용자만 접근할 수 있도록 보호하는 데코레이터입니다.
    
    Args:
        function: 데코레이터가 적용될 함수. 생략할 수 있습니다.
        redirect_field_name: 로그인 후 리다이렉트할 때 사용할 GET 파라미터 이름. 기본값은 'next'입니다.
        login_url: 로그인 페이지 URL. 기본값은 settings.LOGIN_URL입니다.
        
    Returns:
        view_func를 감싸는 데코레이터 함수. function 인자가 주어지면 즉시 데코레이터를 반환합니다.
    """
    actual_decorator = user_passes_test(
        lambda u: u.is_authenticated,  # 사용자가 로그인되었는지 테스트합니다.
        login_url=login_url,
        redirect_field_name=redirect_field_name
    )
    if function:
        return actual_decorator(function)
    return actual_decorator

 

django에서 아래 같이 코드를 작성하면 my_view 함수를 실행하기 전 로그인 여부를 판단하고, 그에 따라 함수를 호출할지, 로그인 페이지로 이동할지 선택합니다.

# views.py
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse

@login_required
def my_view(request):
    return HttpResponse("Hello, you are logged in!")

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('my_view/', views.my_view, name='my_view'),
]

 

 

데이터를 다룰때 사용하는 dataclass 데코레이터도 있습니다.

from dataclasses import dataclass, field

@dataclass
class Person:
    name: str
    age: int = 30
    hobbies: list = field(default_factory=list)

# 사용 예
p1 = Person(name="Alice")
p2 = Person(name="Bob", age=25)
p3 = Person(name="Charlie", hobbies=["reading", "swimming"])

print(p1)  # 출력: Person(name='Alice', age=30, hobbies=[])
print(p2)  # 출력: Person(name='Bob', age=25, hobbies=[])
print(p3)  # 출력: Person(name='Charlie', age=30, hobbies=['reading', 'swimming'])

 

dataclass는 클래스에 __init__(), __repr__(), __eq__() 메서드를 자동으로 추가합니다.

 

 
반응형

'Computer Science > python' 카테고리의 다른 글

추상클래스의 활용  (0) 2024.06.12
프로토콜과 'abc' 모듈  (0) 2024.06.11
seaborn clustermap color label  (0) 2022.05.24
flask_sqlalchemy  (0) 2022.05.23
python 설치  (0) 2022.04.06
반응형

./configure --enable-loadable-sqlite-extensions

 

configure와 make까지 진행했을 때 설치를 더 진행해도 되지만 아래 메세지를 확인하고 설치가 안되는 모듈이 있음을 확인해야함.

 

Python build finished successfully!
The necessary bits to build these optional modules were not found:
_tkinter                                                       
To find the necessary bits, look in setup.py in detect_modules() for the module's name.

 

환경 변수를 설정했음에도 _sqlite3 가 지속적으로 보여서 확인해보니 setup.py 파일에서 직접 수정을 해야 했음.

 

sqlite_incdir = sqlite_libdir = None
sqlite_inc_paths = [ '/usr/include']
반응형

'Computer Science > python' 카테고리의 다른 글

seaborn clustermap color label  (0) 2022.05.24
flask_sqlalchemy  (0) 2022.05.23
Progress bar 모듈 tqdm  (0) 2022.03.07
pandas 활용하기  (0) 2022.02.18
logging 모듈 사용하기  (0) 2022.02.17
반응형

tqdm을 사용해서 얼마나 진행되었는지 작업 진행 정도를 표시함.

 

전체 양을 알 때와 모를 때를 나눠서 표시 할 수 있음.

 

전체 양을 모를 때 

from tqdm import tqdm
with open(filename) as f :
       for index, line in enumerate(tqdm(f, unit='reads', unit_scale=True, mininterval=1)):
               continue

결과물 :

58.9Mreads [00:24, 2.38Mreads/s]

3회 평균 소요 시간 21.7초

 

전체 양을 알 때

from tqdm import tqdm
with open(filename) as f :
        lines = f.readlines()
        for index, line in enumerate(tqdm(lines, total=len(lines), unit='reads', unit_scale=True, mininterval=1)):
                continue

결과물 :

100%|██████████████████████████████████████████████████████████| 58.9M/58.9M [00:15<00:00, 3.88Mreads/s]

3회 평균 소요 시간 30.6초 

 

f.readlines() 함수로 파일 전체를 읽어서 사이즈를 계산하고 진행 했을 때는 이미 메모리에 내용이 올라왔기 때문에 시간 당 읽는 줄 수는 빠르지만 파일을 읽는데 드는 시간으로 인해 총 시간은 더 느림 하지만 전체 진행율을 알 수 있다는 장점이 있음.

 

 

반응형

'Computer Science > python' 카테고리의 다른 글

flask_sqlalchemy  (0) 2022.05.23
python 설치  (0) 2022.04.06
pandas 활용하기  (0) 2022.02.18
logging 모듈 사용하기  (0) 2022.02.17
f-string을 활용한 regex 사용법  (0) 2022.02.15
반응형

log 파일 작성 모듈 logging.

 

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(levelname)s %(asctime)s] %(message)s',"%Y-%m-%d %H:%M:%S")

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
stream_handler.setLevel(logging.INFO)
logger.addHandler(stream_handler)

file_handler = logging.FileHandler(f'my.log')
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)
logger.addHandler(file_handler)

logging.info(f'Read Database File')
logging.debug(f'Read Database File')

 

handler를 여러 개 만들어서 하나는 stdout 다른 하나는 my.log 파일로 만들고 level에 따라 출력 범위를 다르게 조절한다.

 

위의 예시에서는 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 중에 debug는 파일로만 생성되도록 설정되었다.

 

개발 단계에서는 stream_handler를 DEBUG로 놓고 진행하다가 개발 완료시 INFO로 수정하면 원하는 부분만 출력하도록 조정 가능하다.

 

a = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode('UTF-8')
logging.debug(f'{cmd}\n{a}')

subprocess와 연결해서 command를 입력하고 나오는 출력물까지 debug로 한 번에 연결 할 수 있다.

반응형

'Computer Science > python' 카테고리의 다른 글

Progress bar 모듈 tqdm  (0) 2022.03.07
pandas 활용하기  (0) 2022.02.18
f-string을 활용한 regex 사용법  (0) 2022.02.15
Primer 서열 분석을 위한 python 코드  (0) 2021.08.17
String Format으로 길이 고정하기  (0) 2020.06.24
반응형

 

read 서열에서 error를 1이하로 허용하는 내에 BESTMATCH를 찾아 시작과 종료지점 그리고 매치되는 서열을 확인하는 코드. error는 mismatch, insertion, deletion을 의미한다.

 

import regex

primer_seq, read_seq

regex_primer_seq = fr'({primer_seq}{{e<=1}})'
match_object = regex.search(regex_primer_seq, read_seq, regex.BESTMATCH)

match_start, match_end = match_object.span()
match_seq = match_object.captures()
반응형

'Computer Science > python' 카테고리의 다른 글

pandas 활용하기  (0) 2022.02.18
logging 모듈 사용하기  (0) 2022.02.17
Primer 서열 분석을 위한 python 코드  (0) 2021.08.17
String Format으로 길이 고정하기  (0) 2020.06.24
python multi-level argparse  (0) 2019.07.12

+ Recent posts