본문으로 건너뛰기

`__init__`과 self

파이썬 목차클래스레슨 54 / 59

목차

객체를 만들 때마다 card.owner = '민지', card.stamps = 0을 매번 밖에서 따로 적으면 한 줄이라도 빼먹기 쉽습니다. 클래스 안에 def __init__(self, ...): 함수를 만들어 두면, 객체를 생성할 때 파이썬이 자동으로 이 함수를 불러 새 객체(self)의 처음 속성을 채워 줍니다.

__init__은 앞뒤에 밑줄(_)이 두 개씩 붙은 특별한 이름입니다(영어 initialize, '초기화하다'에서 온 이름).

8단계 중 1단계. StampCard('민지')를 부를 때 __init__과 self가 새 객체의 처음 속성을 채우는 과정을 따라갑니다.

1__init__과 self가 하는 일

StampCard('민지')를 부르면 파이썬 안에서는 다음 세 걸음이 차례로 일어납니다.

  1. 빈 StampCard 새 객체 ①을 하나 만듭니다.
  2. __init__(self, owner) 함수를 부르면서, 첫 번째 매개변수 self에는 방금 만든 새 객체 ①을 붙이고 두 번째 매개변수 **owner에는 인자 '민지'**를 붙입니다(규칙 5).
  3. 함수 안의 self.owner = owner와 self.stamps = 0이 새 객체 ① 안에 두 속성을 채운 뒤, 완성된 객체 ①을 돌려줍니다.
class StampCard:
    def __init__(self, owner):
        self.owner = owner
        self.stamps = 0


c1 = StampCard('민지')
c2 = StampCard('준호')
print(c1.owner, c1.stamps)
print(c2.owner, c2.stamps)
실행 결과
민지 0
준호 0

부를 때는 StampCard('민지')처럼 self를 뺀 나머지 인자(owner)만 넘깁니다. 첫 번째 매개변수 self 자리에는 파이썬이 새 객체를 알아서 넣어 주기 때문입니다.

2왜 owner = owner가 아니라 self.owner = owner일까?

함수 안의 이름, 밖의 이름에서 함수 안에서 새로 만든 이름은 함수가 끝날 때 칸과 함께 사라진다고 배웠습니다(규칙 5). 만약 __init__ 안에서 self.을 빼먹고 stamps = 0이라고만 적으면 어떻게 될까요? 무엇이 일어날지 먼저 생각해 보세요.

class StampCard:
    def __init__(self, owner):
        self.owner = owner
        stamps = 0


card = StampCard('민지')
print(card.owner)
실행 결과
민지

self.owner = owner는 self가 가리키는 객체 ① 안에 .owner 속성을 만들었으므로 __init__이 끝난 뒤에도 card.owner로 읽을 수 있습니다. 반면 4번 줄 stamps = 0은 객체 ①이 아니라 __init__ 함수 칸 안에만 잠깐 생겼다가 사라지는 지역 변수일 뿐이라, 객체 ①에는 .stamps 속성이 생기지 않습니다.

예제

처음 스탬프 개수에 기본값 주기

기본값과 키워드 인자에서 배운 매개변수 기본값은 __init__에서도 그대로 쓸 수 있습니다.

class StampCard:
    def __init__(self, s, n=0):
        self.owner = s
        self.stamps = n


a = StampCard('민지')
b = StampCard('준호', 3)
print(a.owner, a.stamps)
print(b.owner, b.stamps)
실행 결과
민지 0
준호 3

StampCard('민지')처럼 주인 이름만 넘기면 n이 기본값 0이 되고, StampCard('준호', 3)처럼 두 번째 인자를 넘기면 처음 스탬프가 3개로 시작합니다.

자주 틀리는 곳

1. __init__ 매개변수에서 self를 빼먹는다

틀린 생각: 부를 때 StampCard('민지')처럼 인자를 하나만 넘기니 def __init__(owner):처럼 매개변수도 하나만 적으면 된다.

파이썬은 객체를 만들 때 새 객체 자신(self)을 늘 첫 번째 인자로 넘깁니다. 따라서 def __init__(owner):라고 적으면 새 객체가 owner에 들어가고 '민지'가 갈 자리가 없어 TypeError가 납니다.

class StampCard:
    def __init__(owner):
        owner.stamps = 0


name = '민지'
card = StampCard(name)
실행 결과
Traceback (most recent call last):  File "main.py", line 7, in <module>    card = StampCard(name)TypeError: StampCard.__init__() takes 1 positional argument but 2 were given

오류 메시지가 1개를 받는데 2개가 넘어왔다고 하는 까닭이 바로 파이썬이 뒤에서 첫 번째 인자로 self(새 객체)를 함께 넘겼기 때문입니다.

맞는 규칙: __init__의 첫 번째 매개변수 자리에는 늘 self를 적고, 부를 때는 self 뒤의 매개변수 값만 넘깁니다.

2. __init__의 밑줄 개수를 틀린다(_init_)

틀린 생각: _init_처럼 밑줄을 앞뒤 하나씩만 적어도 초기화 함수로 동작한다.

파이썬이 객체를 만들 때 자동으로 찾아 부르는 이름은 밑줄이 앞뒤 두 개씩인 __init__뿐입니다. 밑줄을 하나씩만 적으면 평범한 메서드로 취급되어 객체를 만들 때 자동으로 불리지 않습니다.

class StampCard:
    def _init_(self, owner):
        self.owner = owner


name = '민지'
card = StampCard(name)
실행 결과
Traceback (most recent call last):  File "main.py", line 7, in <module>    card = StampCard(name)TypeError: StampCard() takes no arguments

맞는 규칙: 초기화 함수 이름은 앞뒤에 밑줄을 두 개씩 붙여 def __init__(self, ...):⁠로 적습니다.

3. self.을 빼먹고 속성을 만든다

틀린 생각: __init__ 안에 stamps = 0이라고만 적어도 객체의 .stamps 속성이 만들어진다.

self. 없이 적은 이름은 __init__ 함수 칸 안에서만 쓰이다 사라집니다(규칙 5).

class StampCard:
    def __init__(self, owner):
        self.owner = owner
        stamps = 0


card = StampCard('민지')
print(card.stamps)
실행 결과
Traceback (most recent call last):  File "main.py", line 8, in <module>    print(card.stamps)          ^^^^^^^^^^^AttributeError: 'StampCard' object has no attribute 'stamps'

맞는 규칙: 객체 안에 남길 속성은 반드시 self.속성이름 = 값 꼴로 적습니다.

확인 문제

1. 다음 코드를 실행하면 무엇이 출력될까요?

class Menu:
    def __init__(self, name, p):
        self.name = name
        self.price = p + 500


m = Menu('라테', 4000)
print(m.name, m.price)
힌트

Menu('라테', 4000)을 부를 때 name과 p에 무엇이 붙고 self.price에 얼마가 저장되는지 보세요.

정답
실행 결과
라테 4500

self.name에는 '라테'가, self.price에는 4000 + 500인 4500이 저장되어 라테 4500이 출력됩니다.

2. 학생 이름을 받아 self.name에 저장하고 self.score는 0으로 시작하는 __init__을 완성하려 합니다. 빈칸 (가)와 (나)를 채우세요.

class Student:
    def __init__(_(가)_, name):
        self.name = name
        ____(나)____ = 0


s = Student('민지')
print(s.name, s.score)
힌트

(가)는 새 객체 자신을 받는 첫 번째 매개변수이고, (나)는 그 객체 안에 score 속성을 만드는 표현입니다.

정답
  • (가): self
  • (나): self.score

첫 매개변수로 self를 받고 self.score = 0으로 적어야 객체 안에 score 속성이 생깁니다.

3. 아래 코드를 실행하면 7번 줄에서 AttributeError가 납니다. 3번 줄을 어떻게 고쳐야 100이 찍힐까요?

class Player:
    def __init__(self):
        hp = 100


p = Player()
print(p.hp)
힌트

3번 줄 hp = 100이 객체(self)에 속성을 붙이고 있는지 확인해 보세요.

정답

3번 줄을 self.hp = 100으로 고칩니다.

class Player:
    def __init__(self):
        self.hp = 100


p = Player()
print(p.hp)
실행 결과
100

self.을 붙여야 함수가 끝난 뒤에도 p 객체 안에 .hp 속성이 남습니다.

아래 코드에서 StampCard('민지')의 처음 스탬프 수가 1개로 시작하도록 __init__의 self.stamps 줄을 바꿔 돌려 보세요.

처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

파이썬 목차로