[Re:Python] 1. self 이해하기

Magit·2020년 5월 6일
27

Python

목록 보기
1/1

참고 사이트
self 이해하기

class 다시 보기

  • 클래스의 사용 목적 : 변수와 함수를 묶어서 하나의 새로운 객체(타입)으로 만드는 것

  • '클래스를 정의한다.' 의 의미 : 새로운 데이터 타입을 정의한 것. 이를 실제로 사용하려면 인스턴스를 생성해야함.

  • 인스턴스 : 붕어빵 틀(클래스)에 반죽을 넣어서 만들어진 붕어빵

    • 정의된 클래스를 이용해서 인스턴스 생성하기 : 클래스 이름 뒤에 ()를 넣으면 된다.
    >>> card1 = BusinessCard()
    >>> card1
    <__main__.BusinessCard object at 0x0302ABB0>
    • BusinessCard라는 클래스의 인스턴스가 메모리의 한 위치에 생성되고, card1이라는 변수가 이를 바인딩하게 된다.
  • 클래스에 메서드 추가하기

>>> class BusinessCard:
        def set_info(self, name, email, addr):
                self.name = name
                self.email = email
                self.addr = addr
  • 클래스 내부 함수인 메서드를 정의하는 것도 def 키워드를 사용한다.
  • set_info 라는 메서드는 네 개의 인자를 받는데, name, email, addr은 사용자로부터 받은 데이터를 메서드로 전달할 때 사용하는 인자이다. 그렇다면 self는 무엇일까?

self ?

처음에는 어렵기때문에 클래스 내부에 정의된 함수인 메서드의 첫번째 인자는 반드시 self여야 한다. 라는 식으로 외우고 지나갔다.

그러나 메서드의 첫번째 인자가 항상 self 여야한다는 것은 사실 틀린말이다.

먼저 예시로 첫번째 인자가 self가 아닌 메서드와 self인 메서드를 만들어보자. 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에 대한 값은 파이썬이 자동으로 넘겨주기 때문이다.

그렇다면 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
>>>

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)
43219856

생성된 인스턴스가 메모리의 43219856 번지에 있음을 확인 가능하다. (이 값은 실행환경에 영향을 받아서 직접 실행하면 다른 값이 나올 수 있다.)

f와 인스턴스의 관계를 그림으로 나타내면 아래와 같다. Foo 클래스에 대한 인스턴스는 메모리의 43219856번지부터 할당돼있고 변수 f는 인스턴스의 주솟값을 담고 있다.

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

>>> f.func2()
43219856
function 2

실행결과가 43219856 이라는 값이 출력된다! Foo 클래스를 정의할 때 id(self)를 출력하게 했는데, id(self)의 값이 바로 43219856이라는 것이다. 이 값은 f라는 변수가 바인딩하고 있는 인스턴스의 주솟값과 동일하다. 즉, 클래스 내에 정의된 self는 클래스 인스턴스임을 알 수 있다.

객체를 하나 더 만들어보자. 새로 생성한 객체는 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 위치에 인자를 전달하지 않고 메서드를 호출하면 아래와 같은 오류가 발생한다. 오류메세지를 확인하면 func2()를 호출할 때 인자를 하나 빠트렸다는 의미이다. 즉, 인자를 하나 전달해야되는데 전달하지 않아서 오류가 난것이다.

그럼 어떤 값을 전달하면 되는걸까? 앞에서 메서드의 self로 전달되는 것은 인스턴스 자체라고 설명했다. 따라서 클래스에 대한 인스턴스를 생성한 후 해당 인스턴스를 전달하면 된다. 새로운 객체를 만들고 이를 f3이 가리키게 해보자. id(f3) 구문 호출을 통해 f3이 가리키는 객체의 주소가 47789136임을 알 수 있다.

>>> f3 = Foo()
>>> id(f3)
47789136

다시 클래스 이름인 Foo를 이용해 func2 메서드를 호출하는데, 이번에는 새로 생성한 인스턴스를 바인딩한 f3을 인자로 전달해보자.

>>> Foo.func2(f3)
47789136
func2

그럼 인스턴스를 통해 fun2를 호출하는 것과 클래스 이름을 통해 func2를 호출하는 것은 어떤 차이가 있을까? 결론부터 말하면 아무런 차이가 없다. 다만 인스턴스.메서드()클래스.메서드(인스턴스) 냐라는 차이가 있을 뿐이다. 보통은 인스턴스.메서드()를 주로 사용한다.


결론: self는 그래서 뭘까??

self객체의 인스턴스 그 자체를 말한다. 즉, 객체 자기 자신을 참조하는 매개변수인 셈이다. 객체지향 언어는 모두 이걸 메소드에 안보이게 전달하지만, 파이썬은 클래스의 메소드를 정의할 때 self를 명시한다. 메소드를 불러올 때 self는 자동으로 전달된다. self를 사용함으로 클래스내에 정의한 멤버에 접근할 수 있게된다.

인스턴스 변수의 가용범위에 대해서 cll179님 블로그를 참조하기 바란다

profile
이제 막 배우기 시작한 개발자입니다.

7개의 댓글

comment-user-thumbnail
2020년 12월 25일

감사합니다 좋은글이네요

1개의 답글
comment-user-thumbnail
2021년 5월 20일

그냥 보고 넘어갔던 내용인데 이해가 엄청 잘되네요

답글 달기
comment-user-thumbnail
2021년 7월 21일

최고입니다

답글 달기
comment-user-thumbnail
2021년 12월 1일

이해하기 쉽게 정리해주셨네요. 잘 보고 갑니다!

답글 달기
comment-user-thumbnail
2022년 3월 24일

감사합니다

답글 달기
comment-user-thumbnail
2023년 8월 22일

여러 사이트를 통해 self를 이해하기 위해 뒤져보고 있는데 여전히 모르겠네요;; 자기 자신을 굳이 왜 호출해야하는건지..

답글 달기