본문 바로가기

dev_AI_framework

데코레이터와 *args, **kwargs 의 사용,

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned: {result}")
        return result
    return wrapper

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

@log_decorator
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

add(5, 10)
greet("Alice", greeting="Hi")

데코레이터가 다음과 같이 사용될 수 있음;

 

출력 형태

Calling add with args: (5, 10), kwargs: {}
add returned: 15
Calling greet with args: ('Alice',), kwargs: {'greeting': 'Hi'}
greet returned: Hi, Alice!

 

 

초기 프로젝트 진행 시 데코레이터를 통해 확장성, 관리를 하고자 한다면, 임의의 파라미터의 개수, 이름, 형식을 미리 지정하는 것이 아닌, *args **kwargs 로 받아와서 사용해야 겠다....

 

현재 data_processing 의 내용을 구현했음, decorator 를 통해 데이터 형식에 대한 검증, 확인이 앞으로 필요하게 될텐데, 이를 decorator 를 통해 추가, 구현 해보자

 

class AccumulateDecorator:
    def __init__(self, func):
        self.func = func
        self.accumulated_args = []
        self.accumulated_kwargs = {}

    def __call__(self, *args, **kwargs):
        # 새로운 인자를 누적합니다.
        self.accumulated_args.extend(args)
        for key, value in kwargs.items():
            self.accumulated_kwargs[key] = value
        
        # 지금까지 누적된 모든 인자를 사용하여 함수를 호출합니다.
        return self.func(*self.accumulated_args, **self.accumulated_kwargs)

# 함수에 데코레이터를 적용합니다.
@AccumulateDecorator
def display_accumulated(*args, **kwargs):
    print("Args:", args)
    print("Kwargs:", kwargs)

# 첫 번째 호출: 인자 1개 전달
display_accumulated(1)
# 두 번째 호출: 인자 2개 전달
display_accumulated(2, 3)
# 세 번째 호출: 키워드 인자 전달
display_accumulated(a=4, b=5)

 

Args: (1,)
Kwargs: {}
Args: (1, 2, 3)
Kwargs: {}
Args: (1, 2, 3)
Kwargs: {'a': 4, 'b': 5}

인자가 전달되고 누적되는 형식의 확인...

 

class AccumulateDecorator:
    def __init__(self, func):
        self.func = func
        self.accumulated_args = []
        self.accumulated_kwargs = {}
        print(f"AccumulateDecorator 인스턴스가 생성되었습니다: {self}")

    def __call__(self, *args, **kwargs):
        # 새로운 인자를 누적합니다.
        self.accumulated_args.extend(args)
        for key, value in kwargs.items():
            self.accumulated_kwargs[key] = value
        
        # 지금까지 누적된 모든 인자를 사용하여 함수를 호출합니다.
        return self.func(*self.accumulated_args, **self.accumulated_kwargs)

@AccumulateDecorator
def display_accumulated_one(*args, **kwargs):
    print("display_accumulated_one - Args:", args)
    print("display_accumulated_one - Kwargs:", kwargs)

@AccumulateDecorator
def display_accumulated_two(*args, **kwargs):
    print("display_accumulated_two - Args:", args)
    print("display_accumulated_two - Kwargs:", kwargs)

# 두 함수에 대해 각각 호출
display_accumulated_one(1)
display_accumulated_one(2)

display_accumulated_two(10)
display_accumulated_two(20)

출력

AccumulateDecorator 인스턴스가 생성되었습니다: <__main__.AccumulateDecorator object at 0x7f3a0c1fbd90>
display_accumulated_one - Args: (1,)
display_accumulated_one - Kwargs: {}
display_accumulated_one - Args: (1, 2)
display_accumulated_one - Kwargs: {}

AccumulateDecorator 인스턴스가 생성되었습니다: <__main__.AccumulateDecorator object at 0x7f3a0c1fbee0>
display_accumulated_two - Args: (10,)
display_accumulated_two - Kwargs: {}
display_accumulated_two - Args: (10, 20)
display_accumulated_two - Kwargs: {}

 

데코레이터는 데코레이터를 사용하는 함수, 메서드에 종속된다.

 

클래스 위에서도 데코레이터의 호출이 가능...

클래스 데코레이터는 클래스의 정의를 수정하거나 확장하는 데 사용됩니다. 함수 데코레이터와 마찬가지로, 클래스 데코레이터도 클래스의 동작을 변경하거나 추가적인 기능을 부여하는 데 활용됩니다.

def log_methods(cls):
    original_methods = {k: v for k, v in cls.__dict__.items() if callable(v)}

    for name, method in original_methods.items():
        def logged_method(self, *args, **kwargs):
            print(f"Method {name} is called with arguments: {args} {kwargs}")
            return method(self, *args, **kwargs)
        setattr(cls, name, logged_method)

    return cls

@log_methods
class Example:
    def greet(self, name):
        print(f"Hello, {name}!")

    def farewell(self, name):
        print(f"Goodbye, {name}!")

# 인스턴스 생성 및 메서드 호출
example = Example()
example.greet("Alice")
example.farewell("Bob")

출력

Method greet is called with arguments: ('Alice',) {}
Hello, Alice!
Method farewell is called with arguments: ('Bob',) {}
Goodbye, Bob!

 

 

k와 v는 클래스의 __dict__.items() 메서드로 반환된 항목의 키와 값입니다. 클래스의 __dict__ 속성은 클래스에 정의된 모든 속성과 메서드를 포함하는 사전(dictionary)입니다. 이 사전의 items() 메서드는 (키, 값) 쌍으로 이루어진 이터러블 객체(dict_items)를 반환합니다.

k와 v의 역할:

  • k (Key): 클래스 속성의 이름(문자열)입니다. 예를 들어, 클래스의 메서드 이름이나 클래스 변수 이름이 k에 해당합니다.
  • v (Value): 해당 속성의 실제 객체입니다. 예를 들어, 메서드 객체, 클래스 변수 값 등이 v에 해당합니다.

Example.__dict__.items()의 결과

Example.__dict__.items()는 Example 클래스의 속성들을 (키, 값) 쌍으로 반환합니다. 아래는 이 예제 클래스에서 k와 v에 저장되는 값들입니다.

  1. 첫 번째 항목:
    • k: "__module__"
    • v: "__main__"
    • 설명: 이 항목은 클래스가 정의된 모듈의 이름을 나타냅니다.
  2. 두 번째 항목:
    • k: "class_variable"
    • v: 42
    • 설명: class_variable 속성의 이름이 k에 저장되고, 그 값인 42가 v에 저장됩니다.
  3. 세 번째 항목:
    • k: "greet"
    • v: <function Example.greet at 0x...>
    • 설명: greet 메서드의 이름이 k에 저장되고, greet 메서드 자체가 v에 저장됩니다. 이 값은 함수 객체로서, 메모리 주소를 포함합니다.
  4. 네 번째 항목:
    • k: "farewell"
    • v: <function Example.farewell at 0x...>
    • 설명: farewell 메서드의 이름이 k에 저장되고, farewell 메서드 자체가 v에 저장됩니다.
  5. 다른 항목들:
    • k: "__dict__", "__weakref__", "__doc__" 등의 특별한(기본적으로 포함된) 클래스 속성들이 있습니다.
    • v: 이들 속성의 값들입니다. 예를 들어, __dict__는 클래스의 속성들을 담은 사전이고, __doc__는 클래스의 문서 문자열입니다.

 

설명:

  • **k**에는 속성 이름이, **v**에는 해당 속성의 값이 저장됩니다.
  • greet와 farewell 메서드는 callable(v) 조건을 통과하는 항목들입니다. 그 외의 class_variable과 특별 속성(__module__, __dict__, __doc__ 등)은 callable(v) 조건에 의해 필터링됩니다.

이로써, k와 v가 각각 클래스의 속성 이름과 속성 값이라는 것을 알 수 있습니다. 이 정보는 클래스의 메서드나 특정 속성만을 필터링하여 작업을 수행하는 데 유용합니다.

 

실제 적용하려면... data_processing 클래스 내에는 각 함수가 존재. 클래스에 데코레이터를 지정하고 실행, 관리를 하는게 나을려나...

아니면 각 함수위에 데코레이터를 작성할지. 

데이터 전처리 시 객체를 생성하지 않으므로 data_processing 클래스 자체는 필요 없을 수 있겠네.

data_processing 클래스는 만들지말고 함수들을 만들어놓고 사용하자.

다른 Framework 에서도 이러한 방식을 사용하는 것을 확인

 

from data_processing import method_name 

을 통해 호출하고,

data_processing_method 가 반드시 포함되어야 할 작업들의 데코레이터 작성, 적용

데이터 검증 decorator 를 작성하자 

논리적 경로명의 적용, 실제 파일 위치와는 상관없이 함수, 클래스에 논리적 경로명을 지정하여 해당 경로로 사용자가 접근할 수 있도록