본문 바로가기

Development/Python

[Python] class - 3) self 이해하기

728x90

먼저 다음과 같이 두 개의 메서드가 정의된 Foo 클래스를 만듭니다. 여기서 func1() 메서드의 첫번째 인자가 self가 아님에도 클래스를 정의할 때 에러가 발생하지 않습니다.

class Foo:
        
        def func1():
                print("function 1")
        
        def func2(self):
                print("function 2")

일단 클래스를 정의했으니 해당 클래스에 대한 인스턴스를 생성하겠습니다. 그리고 생성된 인스턴스를 통해 인스턴스 메서드를 호출하겠습니다. Foo 클래스의 func2 메서드는 메서드의 인자가 self 뿐이므로 실제 메서드를 호출할 때는 인자를 전달할 필요가 없습니다.

>>> f = Foo()
>>> f.func2()
function 2

위 코드에서 메서드를 호출한 결과를 보면 화면에 정상적으로 'function 2'가 출력된 것을 볼 수 있습니다.

참고로 func2 메서드의 첫 번째 인자는 self지만 호출할 때는 아무것도 전달하지 않는 이유는 첫 번째 인자인 self에 대한 값은 파이썬이 자동으로 넘겨주기 때문입니다.

그렇다면 func 1 메서드처럼 메서드를 정의할 때부터 아무 인자도 없는 경우에는 어떻게 될까요? 다음과 같이 인스턴스를 통해 func1()를 호출해보면 오류가 발생합니다. 오류 메세지를 살펴보면 "func1()은 인자가 없지만 하나를 받았다"라는 것을 볼 수 있습니다. 이는 앞서 설명한 것처럼 파이썬 메서드의 첫 번째 인자로 항상 인스턴스가 전달되기 때문에 발생하는 문제입니다.

>>> f.func1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func1() takes 0 positional arguments but 1 was given
>>>

이번에는 self의 정체를 좀 더 확실히 밝혀보기 위해 파이썬 내장 함수인 id를 이용해 인스턴스가 메모리에 할당된 주소값을 확인해보겠습니다. 다음 코드처럼 Foo 클래스를 새로 정의합니다. func2 메서드가 호출될 때 메서드의 인자로 전달되는 self의 id 값을 화면에 출력하는 기능이 추가됐습니다.

>>> class Foo:
        def func1():
                print("function 1")

        def func2(self):
                print(id(self))
                print("function 2")

>>>

Foo 클래스를 새롭게 정의했으므로 인스턴스를 다시 만든 후 id() 내장함수를 이용해 인스턴스가 할당된 메모리 주소를 확인해 봅시다.

>>> f = Foo()
>>> id(f)
3025282528776

생성된 인스턴스가 메모리의 3025282528776번지에 있음을 확인할 수 있습니다. 참고로 이 값은 해당 코드의 실행 환경에 영향을 받게 되므로 여러분이 직접 실행했을 때 이 값과 다른 값이 나올 수 있습니다.

위 코드에서 f와 생성된 인스턴스의 관계를 그림으로 나타내면 그림 6.8과 같습니다. Foo 클래스에 대한 인스턴스는 메모리의 3025282528776번지로부터 할당돼 있고 변수 f는 인스턴스의 주소값을 담고 있습니다. 일단 인스턴스가 할당된 메모리 주소값을 기억해두기 바랍니다.

그림 6.8 인스턴스의 메모리 주소 확인

이번에는 인스턴스 f를 이용해 func2 메서드를 호출해보기 바랍니다. 다음 코드를 살펴보면 func2 메서드를 호출할 때 아무런 값도 전달하지 않았습니다.

>>> f.func2()
3025282528776
function 2
>>>

실행 결과를 보면 3025282528776이라는 값이 출력되는 것을 확인할 수 있습니다. Foo 클래스를 정의할 때 id(self)를 출력하게 했는데 id(self)의 값이 바로 3025282528776인 것입니다. 이 값은 그림 6.8에서 43219856에 해당하는 값입니다. f라는 변수가 바인딩하고 있는 인스턴스의 주소값과 동일합니다. 즉, 클래스 내의 정의된 self는 클래스 인스턴스임을 알 수 있습니다.

아직 이 부분이 잘 이해가 되지 않는다면 객체를 하나 더 만들어보겠습니다. 새로 생성한 객체는 f2가 가리키고 있는데, id를 통해 확인해 보니 주소가 3025282469000입니다. f2를 통해 func2 메서드를 호출해보면 3025282469000가 출력됩니다. 이 값은 바로 f2가 가리키고 있는 객체를 의미합니다.

>>> f2 = Foo()
>>> id(f2)
3025282469000
>>> f2.func2()
3025282469000
function 2
>>>

파이썬의 클래스는 그 자체가 하나의 네임스페이스이기 때문에 인스턴스 생성과 상관없이 클래스 내의 메서드를 직접 호출할 수 있습니다( 네임스페이스는 다음 절에서 설명합니다 ).

>>> Foo.func1()
function 1
>>>

위 코드는 func1 메서드를 호출했지만 앞서 인스턴스를 통해 메서드를 호출했던 것과는 달리 오류가 발생하지 않는 것을 확인할 수 있습니다. 왜냐하면 인스턴스.메서드() 형태로 호출한 것과 달리 이번에는 클래스명.메서드() 형태로 호출했기 때문입니다. 그렇다면 func2()에 대해서도 클래스 이름을 통해 호출해 봅시다. 그림 6.9와 같이 클래스 이름을 통해 func2() 메서드를 호출하려고 하면 self 위치에 인자를 전달해야 한다고 파이썬 인터프리터가 알려줍니다.

그림 6.9 클래스 이름을 이용한 메서드 호출

self 위치에 인자를 전달하지 않고 메서드를 호출하면 그림 6.10과 같이 오류가 발생합니다. 오류 메세지를 확인하면 func2()를 호출할 때 인자를 하나 빠트렸음을 알 수 있습니다. 즉, 인자를 하나 전달해야 하는데 전달하지 않아서 오류가 발생한 것입니다.

그림 6.10 클래스 이름을 이용한 메서드 호출 에러

그럼 도대체 어떤 값을 전달하면 되는 걸까요? 앞에서 메서드의 self로 전달되는 것은 인스턴스 자체입니다. 따라서 클래스의 인스턴스를 생성한 후 해당 인스턴스를 전달하면 됩니다. 다음과 같이 새로운 객체를 만들고 이를 f3이 가리키게 합니다. id(f3) 구문 호출을 통해 f3이 가리키는 객체의 주소가 3025282528840임을 확인할 수 있습니다.

>>> f3 = Foo()
>>> id(f3)
3025282528840

다시 한 번 클래스 이름인 Foo를 이용해 func2 메서드를 호출해보겠습니다. 앞서 func2 메서드는 인자를 하나 필요로 하며, 해당 인자는 인스턴스여야 한다고 말씀드렸습니다. 현재 f3은 새로 생성한 인스턴스를 바인딩하고 있으므로 func2 메서드의 인자로 f3을 전달하면 됩니다.

>>> Foo.func2(f3)
3025282528840
function 2
>>>

그렇다면 인스턴스를 통해 func2를 호출하는 것과 클래스 이름을 통해 func2를 호출하는 것은 어떤 차이가 있을까요?

결로부터 말씀드리면 둘 사이에는 아무런 차이가 없습니다. 다만 '인스턴스.메서드()'냐 '클래스.메서드(인스턴스)'냐라는 차이가 있을 뿐입니다. 보통은 '인스턴스.메서드()'와 같은 방식을 주로 사용합니다.

>>> f3.func2()
3025282528840
function 2
>>>

 

출처: wikidocs.net/1742

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

728x90

'Development > Python' 카테고리의 다른 글

[Python] *args와 **kwargs  (0) 2021.02.14
[Python] print  (0) 2021.01.25
[Python] rfind  (0) 2021.01.21