개발자로 후회없는 삶 살기

파이썬 심화 PART.스페셜 메서드, callable, 정적 메서드 본문

[AI]/[Python]

파이썬 심화 PART.스페셜 메서드, callable, 정적 메서드

몽이장쥰 2023. 11. 2. 16:34

서론

정말로 AI에 필요한 파이썬 심화를 공부합니다.

 

본론

- 스페셜 메서드

스페셜 메서드는 파이썬 객체들이 동일하게 가지는 인터페이스입니다. 파이썬에 있는 여러 내장 함수들이 호출하는 메서드를 사용자 정의 객체에 정의해서 사용자 정의 객체가 내장 함수를 사용할 수 있도록 합니다.

 

예를 들면, 객체 안에 __len__() 스페셜 메서드를 정의하면 파이썬 내장 함수인 len()을 사용할 수 있고, len()은 obj안에 정의된 __len__() 메서드를 호출합니다. 사용자 객체를 만들 때 재정의 하지 않으면 obj를 상속받은 스페셜 메서드가 수행되고 재정의하면 원하는 대로 동작하도록 할 수 있으며, 재정의 하더라도 파이썬 프레임워크의 기능대로 수행이 됩니다.

 

=> 예시

즉, 특수한 예약 함수로 보통은 파이썬 프레임워크 인터페이스라서 사용 설명서에 맞게 쓰기만 하면 스페셜 메서드의 기능을 사용할 수 있습니다.

 

1) __del__

class Person():
    inhabit = "한국"
    __age = 25

    def __init__(self, name, age, status):
        self.name = name
        self.age = age
        self.status = status

    def __del__(self):
        print("나 소멸")

객체를 소멸시킬 때 사용합니다.

 

p가 사라진 것을 확인할 수 있습니다. 메서드를 만들고 내부에 len(obj)를 하는게 아니라 파이썬 프레임워크 내에서 정해진 기능대로 동작합니다.

 

2) __repr__

class Person():
    inhabit = "한국"
    __age = 25

    def __init__(self, name, age, status):
        self.name = name
        self.age = age
        self.status = status

    def __repr__(self):
        return self.inhabit

return 문을 만들고

 

p = Person("a", 1, "2")
print(p)

 

객체를 호출하면 반환 값이 출력됩니다. 자바의 toString과 같은 역할입니다.

_str__도 똑같이 동작합니다.

 

3) __add__

객체 2개를 받아서 더하는 함수입니다.

 

- callable 알아보기

파이썬에선 어떤 객체도 호출 할 수 있습니다. 그 객체를 호출하기 위해서는 그 객체의 내부에 __call__()이 구현이 되어 있어야하고 파이썬에서는 변수에 할당할 수 있는 모든 것은 객체라 함수를 호출하듯이 객체를 호출할 수 있습니다.

 

-> callable() 함수

먼저 Python built-in으로 제공하는 callable() 함수를 살펴봅니다. callable()은 전달 받은 인자가 callable 객체라면 T를 반환하고 아니면 F를 반환합니다. 

 

타입이 function인 f 객체를 인자로 전달하면 T가 나옵니다. callable이란, 말 그래도 호출할 수 있는을 의미합니다. 파이썬의 모든 변수에 할당 가능한 것은 객체라서 객체를 호출할 수 있다면 모든 변수에 할당가능한 것들을 호출할 수 있을 것입니다.

 

숫자 1은 상수이기 때문에 호출할 수 없습니다. 즉, 1은 callable이 아닙니다. 그래서 1()을 하면 에러가 발생합니다.

 

문제는 파이썬에서는 함수도 객체이고 따라서 다른 함수의 인자로 전달할 수 있습니다. hab을 () 없이 인자로 전달하여 함수 호출 결과가 아니고 function 타입 함수 객체가 전달됩니다. 그리고 result 내부에서 () 호출합니다.

 

-> Class

파이썬에서는 Class도 callable입니다.

객체를 생성할 때 Class를 함수처럼 생성자로 호출합니다.

 

당연히 객체 p는 p()로 호출할 일이 없으니 callable이 아닙니다.

 

def __call__(self, *args, **kwargs):
    print("i am callable")

하지만 p를 callable로 만들 수 있습니다. Class에 __call__()를 추가하면 됩니다. 파이썬은 해당 객체가 __call__()을 가지고 있는지 보고 있다면 호출합니다.

 

아니라면 is not callable 에러가 발생합니다.

 

-> __call__()은 언제 유용할까?

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

device = "cuda" if torch.cuda.is_available() else "cpu"
model = NeuralNetwork().to(device)

X = torch.rand(1, 28, 28, device=device)
logits = model(X)

PyTorch에서 이러한 유형을 자주 보게 됩니다. model = 부분을 보면 model은 객체라서 callable이 아니지만 model(X)를 보면 callable인 것을 알 수 있습니다. Pytorch에 nn.Module Class가 내부적으로 __call__()를 정의해두었습니다. 그러면 model을 호출하면 forward()가 수행됩니다.

 

PyTroch에서는 nn.module에서 forward()를 구현해두었는데 __call__()함수가 forward()를 호출합니다. __call__()에는 forward 호출 전 후에 처리할 것들이 작성되어 있습니다.

 

- 객체와 클래스의 관계 확인하기

issubclass 내장 메서드로 상속관계를 확인할 수 있습니다.  T 입니다.

 

- type 메타 클래스

파이썬에서 작성된 모든 클래스는 type 클래스로 만들어지며, 이는 상속 관계가 아니라 생성 관계입니다. 이는 isinstance로 확인합니다. T 입니다. 

 

Object 클래스도 type 메타 클래스에 의해서 만들어집니다.

 

- dataclass로 클래스 정의하기

생성자로 클래스를 생성할 때 속성이 너무 많아지면 생성자로 하나하나 만드는 것이 복잡하고 지저분해지는 문제를 타입 힌트만으로 되도록 파이썬이 제공합니다. 자바의 롬복과 비슷한 역할입니다.

 

위에 dataclass만 붙이면 이걸 속성으로 다 가지는 생성자가 자동으로 만들어집니다. __dict__로 확인해보면 클래스 수준 속성에도 보이고

 

객체를 생성도 되어서 객체 속성에도 보입니다. 파이썬은 namespace가 공개되어 있어서 어느 시점에서도 속성을 추가할 수 있습니다. 

 

- 정적 메소드

클래스 단위 메서드가 정적 메서드인데 파이썬에서는 2가지로 표현할 수 있습니다.

 

1) 인스턴스 메서드

def add_instance_method(self, a, b):
    return a + b
    
p = Person("한상범", 25, "hh")
result = p.add_instance_method(10, 5)
print(result)

 

2) classmethod

@classmethod
def add_class_method(cls, a, b):
    return a + b
    
Person.add_class_method(3, 5)

정적 메서드 첫번째 방법입니다. cls는 무시하고 접근해야 합니다.

 

3) staticmethod

@staticmethod
def add_static_method(a, b):
    return a + b

Person.add_static_method(3, 5)

정적 메서드 두 번째 방법입니다. 이 둘의 차이가 필요할 때 공부해야 합니다.

Comments