[인공지능사관학교: 자연어분석A반] 학습 내용 보충 - 클래스(Class)와 __call__ 함수 정리

Suhyeon Lee·2025년 7월 18일

참고 1 참고 2

클래스(Class)와 객체(Object)

  • 클래스: 붕어빵 틀
  • 객체: 붕어빵 틀로 만든 붕어빵
    • 객체마다 고유한 성격을 가짐
      • 동일한 클래스로 만든 객체들은 서로 영향을 전혀 주지 않는다는 의미
  • 클래스 사용 목적
    • 변수와 함수를 묶어 하나의 새로운 객제(타입) 만듦
  • '클래스를 정의한다'의 의미
    • 새로운 데이터 타입을 정의한 것
    • 이를 실제로 사용하려면 인스턴스를 생성해야 함
      • 클래스 이름 뒤에 () 넣기

객체와 인스턴스의 차이

a = Calc()
  • 어떤 것이 주체인가에 따라 다름
    • a는 객체이다 (O)
    • a 객체는 Calc의 인스턴스이다(O)
    • a는 객체이고 Calc의 인스턴스이다(O)
    • a는 인스턴스이다(X)
  • Calc라는 클래스의 인스턴스가 메모리의 한 위치에 생성되고, a라는 변수가 이를 바인딩하게 됨

클래스의 구조

class Calc:
	def set(self, n1, n2):
    	self.n1 = n1
        self.n2 = n2
    def 함수명(매개변수):
    	수행할 문장
  • 클래스 안에 만들어진 함수는 메서드(Method)라 부름

메서드(Method)

  • 위의 set 메서드를 보면 self, n1, n2를 입력 값으로 받음
    • 첫 번째 매개변수인 self는 특별한 의미가 있음(함수와의 차이점)
    • 메서드의 첫 번째 매개변수는 관례적으로 self를 사용(다른 단어여도 된다고 함)
a = Calc()
a.set(10,5)
  • 객체를 통해 메서드를 호출하려면 .(도트 연산자; dot operator)를 사용하여 호출
    • 자세히 보면 호출했을 때 self를 입력으로 받지 않았음
      • a.set(10,5)로 호출했르 때 self 매개변수는 호출한 객체 a로 자동으로 전달되기 때문
      • self는 객체의 인스턴스 그 자체를 말함 (즉, 객체 자기 자신을 참조하는 매개변수인 셈)
      • 객체지향언어는 모두 이걸 메서드에 안 보이게 전달하지만, 파이썬은 클래스의 메서드를 정의할 때 self를 명시하고 메서드를 불러올 때 self를 자동으로 전달함
      • self를 사용하는 것으로 클래스 내에 정의한 멤버에 접근 가능
a.n1 = 10
a.n2 = 5
  • 객체 변수
    • 객체에 생성되는 객체만의 변수
    • set 메서드가 실행되면 a 객체에 객체변수 n1과 n2가 생성되고 10, 5의 값이 저장됨

※ '동일한 클래스로 만든 객체들은 서로 영향을 전혀 주지 않는다.'에 대하여:

a=Calc()
b=Calc()

a=set(10,5)
b=set(6,3)
  • 동일한 클래스로 만든 a와 b에서 돌일한 메서드를 이용했을 때 각 객체가 서로 영향을 끼치지 않음
    • a.n1=10이며 b.n1=6이라는 의미

self?

  • Foo 클래스를 만들고 첫 번째 인자가 self가 아닌 메서드와 self인 메서드 비교해보기
    • 클래스를 만들 때 func1() 메서드의 첫 번째 인자가 self가 아님에도 에러가 발생하지 않는다
>>> class Foo:
        def func1():
                print("function 1")
        def func2(self):
                print("function 2")
  • 해당 클래스의 인스턴스를 생성하고 생성된 인스턴스를 통해 인스턴스 메서드를 호출해보기
    • Foo 클래스의 func2 메서드는 메서드의 인자가 self뿐이므로 실제 메서드를 호출할 때 인자를 전달할 필요가 없음
    • 메서드를 호출한 결과를 보면 화면에 정상적으로 'function 2'가 출력되는걸 알 수 ㅇ,ㅁ
      • 참고로 func2 메서드의 첫 번째 인자는 self지만 호출할 때는 아무것도 전달하지 않는 이유는 첫 번째 인자인 self에 대한 값은 파이썬이 자동으로 넘겨주기 때문임
>>> f = Foo()
>>> f.func2()
function 2
  • func1 메서드처럼 메서드를 정의할 때부터 아무 인자도 없는 경우에는 어떻게 될까?
    • 인스턴스를 통해 func1() 을 호출하면 오류가 발생
      • 오류메시지는 "func1()은 인자가 없지만 하나를 받았다." 라는 의미
      • 이는 앞서 설명한 것처럼 파이썬 메서드의 첫번째 인자로 항상 인스턴스가 전달되기 때문임
>>> f.func1()
Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    f.func1()
TypeError: func1() takes 0 positional arguments but 1 was given
  • 파이썬 내장 함수 id를 이용해 인스턴스가 메모리에 할당된 주솟값을 확인해보기
    • Foo 클래스를 새롭게 정의하고 인스턴스를 다시 만들고 id() 내장함수를 이용해 인스턴스가 할당된 메모리 주소를 확인
>>> class Foo:
        def func1():
                print("function 1")

        def func2(self):
                print(id(self))
                print("function 2")
>>> f = Foo()
>>> id(f)
43219856
  • 생성된 인스턴스가 메모리의 43219856 번지에 있음
    • f와 인스턴스의 관계: Foo 클래스에 대한 인스턴스는 메모리의 43219856번지부터 할당돼있고 변수 f는 인스턴스의 주솟값을 담고 있음
  • 인스턴스 f를 이용해서 func2 메서드를 호출
    • func2 메서드를 호출할 때 아무런 값도 전달하지 않음
    • f라는 변수가 바인딩하고 있는 인스턴스의 주솟값과 동일한 값이 출력됨
    • 즉, 클래스 내에 정의된 self는 클래스 인스턴스임을 알 수 있음
>>> f.func2()
43219856
function 2
  • 객체를 하나 더 만들어 비교해보기
    • 새로 생성한 객체는 f2가 가리키고 있는데 id를 통해 주소가 47789232인걸 확인할 수 있음
    • f2를 통해 func2 메서드를 호출하면 똑같이 47789232 가 출력됨
      • 이 값은 f2가 가리키고 있는 객체를 의미
>>> f2 = Foo()
>>> id(f2)
47789232
>>> f2.func2()
47789232
function 2
  • 파이썬의 클래스는 그 자체가 하나의 네임스페이스 이기 때문에 인스턴스 생성과 상관없이 클래스 내의 메서드를 직접 호출할 수 있음
>>> Foo.func1()
function 1
  • func1 메서드를 호출했지만 앞서 인스턴스를 통해 메서드를 호출했던 것과 달리 오류가 발생하지 않음
    • 앞서 인스턴스.메서드() 형태로 호출한 것과 달리 이번에는 클래스명.메서드() 형태로 호출했기 때문!
  • func2에서도 클래스 이름을 통해 호출해보면 self 위치에 인자를 전달하지 않고 메서드를 호출했을 때 오류가 발생
    • 인자를 하나 전달해야 하는데 전달하지 않아서 오류가 난 것
    • 그럼 어떤 값을 전달하면 되는 걸까?
      • 앞에서 메서드의 self로 전달되는 것은 인스턴스 자체였음
      • 따라서 클래스에 대한 인스턴스를 생성한 후 해당 인스턴스를 전달하면 됨
>>> f3 = Foo()
>>> id(f3)
47789136
>>> Foo.func2(f3)
47789136
func2
  • 인스턴스를 통해 fun2를 호출하는 것과 클래스 이름을 통해 func2를 호출하는 것은 어떤 차이가 있을까?
    • 결론부터 말하면 아무런 차이가 없음
    • 다만 인스턴스.메서드()클래스.메서드(인스턴스) 냐라는 차이가 있을 뿐
      • 보통은 인스턴스.메서드()를 주로 사용

생성자(Constructor)

  • 객체가 생성될 때 자동으로 호출되는 메서드를 의미
class Calc:
	def set(self, n1, n2):
    	self.n1 = n1
        self.n2 = n2
    def add(self, n1, n2):
    	return self.n1 + self.n2
  • Calc 클래스에 add라는 더하기 메서드를 만든 후 set으로 n1과 n2를 정의하지 않고 add를 쓰면 오류 발생
    • 이를 방지하기 위해 메서드 이름으로 __init__을 사용하면 해당 메서드는 생성자가 됨
class Calc:
	def __init__(self, n1, n2):
    	self.n1 = n1
        self.n2 = n2
	def set(self, n1, n2):
    	self.n1 = n1
        self.n2 = n2
    def add(self, n1, n2):
    	return self.n1 + self.n2
  • __init__은 set과 이름만 다르고 모든 게 동일하지만 이름을 __init__으로 하고 메서드를 생성했기 때문에 생성자로 인식되어 클래스가 실행되면 자동으로 호출
  • __init__이 있으면 Calc 클래스 호출 시 매개변수 값을 입려갷 주어야 함: a-Calc(10,5)
    • 생성자가 있기 때문에 매개변수 없이 클래스를 호출한다면 오류 발생

__call__

  • __init__과 같은 생성자
  • 함수를 호출하는 것처럼 클래스의 객체를 호출하게 만들어주는 메서드
  • 즉, __init__은 인스턴스 초기화를 위해, __call__은 인스턴스 호출 시 실행을 위해 작성하는 메서드임
class Calc:
    def __init__(self, n1, n2):
        self.n1 = n1
        self.n2 = n2
        return print(self.n1, self.n2)
    def __call__(self, n1, n2):
        self.n1 = n1
        self.n2 = n2
        return print(self.n1 + self.n2)
s = Calc(1,2)
s(7,8)
1 2
15

클래스의 상속(inheritance)

  • 어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만든 것
  • class 클래스 이름(상속할 클래스 이름)으로 상속
class GoodCalc(Calc):
	pass
  • GoodCalc는 Calc을 상속했기 때문에 add와 set 등 Calc의 모든 기능을 사용할 수 있음
    • 즉, 상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 변경할 때 사용

메서드 오버라이딩(overriding)

  • 부모 클래스(상속한 클래스)에 있는 메서드를 동일한 이름으로 다시 만드는 것을 의미
  • 메서드 오버라이딩(=덮어쓰기)을 하면 부모 클래스의 메서드 대신 오버라이딩한 메서드가 호출됨
    • 부모 클래스의 메서드를 같은 이름으로 수정하고 호출할 때 사용
      • 예: torch.nn.Module의 forward 메서드
profile
2 B R 0 2 B

0개의 댓글