Intro

이 글에서는 어떤 클래스의 attribute(필드나 메서드)를 그 클래스 바깥에서, “문자열” 만으로 쉽게 호출해오는 방법을 소개하겠습니다.

이 글은 임커밋님의 유튜브 영상 약간 건방?져보이는 함수 를 참고하였습니다.

getattr

  • getattr 은 어떤 클래스(혹은 인스턴스)의 attribute(필드나 메서드)를 가져오는 함수입니다.
  • 예를 들면 아래와 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DummyClass:    
    def __init__(self):
        self.description = "이 클래스는 간단한 사칙연산을 담당합니다."
    def add(self, a:int, b:int): return a + b
    def sub(self, a:int, b:int): return a - b
    def mul(self, a:int, b:int): return a * b
    def div(self, a:int, b:int): return a / b

# 필드에 접근
dc = DummyClass()
description = getattr(dc, "description")
print(description)

# 메서드에 접근
add_ex = getattr(dc, "add")
print(add_ex(10, 1))
1
2
3
# 출력
이 클래스는 간단한 사칙연산을 담당합니다.
11

응용

(1) 단일 로직으로 클래스의 함수를 동적으로 불러오기

  • getattr은 문자열로 클래스의 속성을 가져올 수 있습니다.
  • 이 기능을 응용해서 짧은 코드로, 클래스의 함수를 동적으로 불러오는 코드를 작성해보겠습니다.
1
2
3
4
5
6
7
8
9
10
def dynamic_dummy_func(method:str, a:int, b:int):
    dc = DummyClass()
    func = getattr(dc, method)
    return func(a, b)

# 함수 외부에서 각 method별 계산 수행
print(dynamic_dummy_func("add", 10, 1))
print(dynamic_dummy_func("sub", 10, 1))
print(dynamic_dummy_func("mul", 10, 1))
print(dynamic_dummy_func("div", 10, 1))
1
2
3
4
5
# 출력
11
9
10
10.0

(2) 변수에 넣지 않고 바로 함수로 사용할 수 있음

1
2
dc = DummyClass()
print(getattr(dc, "add")(10, 2))
1
2
# 출력
12

(3) 정적 타입 힌트를 통해 접근할 수 있는 속성을 제한하면 좋다.

1
2
3
4
5
6
7
8
9
10
Attrs = [attr for attr in dir(DummyClass) if not attr.startswith("_")]

def func(method, a, b):
    if method in Attrs: # method에 대한 검증을 위함
        dc = DummyClass()
        print(getattr(dc, method)(a, b))
    else:
        print(f"Invalid method: {method}")

func("wrong method", 1, 2)
1
2
# 출력
Invalid method: wrong method

(4) 응용

  • requests에 응용할 수 있다.
  • requests는 HTTP 요청을 보내고 응답을 받아오는 라이브러리인데, 이 요청의 method는 get, post 등 여러 가지가 있다.
  • 이를 아래와 같이 통일되게 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
# 기존의 requests 사용법
import requests
response = requests.get(url)
response = requests.post(url, json=body)

# getattr을 이용하기
response = getattr(requests, "get")(url)
response = getattr(requests, "post")(url, json=body)

# --> 기존의 requests 에서는 HTTP 통신 방법(get, post...)이 바뀌면 사용하는 함수를 바꿔야 했음
# --> getattr을 이용하면 동일한 로직(함수)를 쓰면서, 함수에 사용되는 인자들만 바꿔주면 됨

기타

  • 비슷하게 Java에는 reflection 이 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.lang.reflect.*;

class Dummy {
    public int value = 10;
    public int add(int a, int b) {
        return a + b;
    }
}

public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        Dummy d = new Dummy();

        // (1) 필드 접근
        Field field = d.getClass().getField("value");
        System.out.println(field.get(d));  // -> 10

        // (2) 메서드 호출
        Method method = d.getClass().getMethod("add", int.class, int.class);
        Object result = method.invoke(d, 3, 5);
        System.out.println(result);        // -> 8
    }
}

Reference

임커밋 - 약간 건방?져보이는 함수

Comments