34.2 속성 사용하기

지금까지 클래스에서 메서드를 만들고 호출해보았습니다. 이번에는 클래스에서 속성을 만들고 사용해보겠습니다. 속성(attribute)을 만들 때는 __init__ 메서드 안에서 self.속성에 값을 할당합니다.

class 클래스이름:
    def __init__(self):
        self.속성 = 

이제부터 소스가 길어지니 스크립트 파일로 만들겠습니다. 다음 내용을 IDLE의 소스 코드 편집 창에 입력한 뒤 실행해보세요.

class_attribute.py

class Person:
    def __init__(self):
        self.hello = '안녕하세요.'
 
    def greeting(self):
        print(self.hello)
 
james = Person()
james.greeting()    # 안녕하세요.

실행 결과

안녕하세요.

Person 클래스의 __init__ 메서드에서 self.hello'안녕하세요.' 인삿말 넣었습니다.

class Person:
    def __init__(self):
        self.hello = '안녕하세요.'

__init__ 메서드는 james = Person()처럼 클래스에 ( )(괄호)를 붙여서 인스턴스를 만들 때 호출되는 특별한 메서드입니다. 즉, __init__(initialize)이라는 이름 그대로 인스턴스(객체)를 초기화합니다.

특히 이렇게 앞 뒤로 __(밑줄 두 개)가 붙은 메서드는 파이썬이 자동으로 호출해주는 메서드인데 스페셜 메서드(special method) 또는 매직 메서드(magic method)라고 부릅니다. 앞으로 파이썬의 여러 가지 기능을 사용할 때 이 스페셜 메서드를 채우는 식으로 사용하게 됩니다.

이제 greeting 메서드를 살펴보겠습니다. greeting 메서드에서는 printself.hello를 출력하도록 만들었습니다.

    def greeting(self):
        print(self.hello)

그다음에 Person 클래스로 인스턴스를 만들고, greeting 메서드를 호출해보면 self.hello에 저장된 '안녕하세요.'가 출력됩니다.

james = Person()
james.greeting()    # 안녕하세요.

지금까지 __init__ 메서드에서 속성을 만들고 greeting 메서드에서 속성을 사용해봤습니다. 속성은 __init__ 메서드에서 만든다는 점과 self.(점)을 붙인 뒤 값을 할당한다는 점이 중요합니다. 클래스 안에서 속성을 사용할 때도 self.hello처럼 self에 점을 붙여서 사용하면 됩니다.

34.2.1  self의 의미

그런데 도데체 self는 뭘까요? self는 인스턴스 자기 자신을 의미합니다. 우리는 인스턴스가 생성될 때 self.hello = '안녕하세요.'처럼 자기 자신에 속성을 추가했습니다. 여기서 __init__의 매개변수 self에 들어가는 값은 Person()이라 할 수 있습니다. 그리고 self가 완성된 뒤 james에 할당됩니다. 이후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수 self에 들어옵니다. 그래서 greeting 메서드에서 print(self.hello)처럼 속성을 출력할 수 있었던 것입니다.

인스턴스와 self의 관계를 그림으로 나타내면 다음과 같은 모양이 됩니다.

그림 34-4 인스턴스와 self

지금까지 만든 클래스는 인사만 할 줄 아는 단순한 클래스였습니다. 클래스로 인스턴스를 만들어봐야 다 똑같이 '안녕하세요.'만 출력할 뿐입니다.

34.2.2  인스턴스를 만들 때 값 받기

이번에는 클래스로 인스턴스를 만들 때 값을 받는 방법을 알아보겠습니다. 다음과 같이 __init__ 메서드에서 self 다음에 값을 받을 매개변수를 지정합니다. 그리고 매개변수를 self.속성에 넣어줍니다.

class 클래스이름:
    def __init__(self, 매개변수1, 매개변수2):
        self.속성1 = 매개변수1
        self.속성2 = 매개변수2

그럼 Person 클래스로 인스턴스를 만들 때 이름, 나이, 주소를 받아보겠습니다.

class_init_attribute.py

class Person:
    def __init__(self, name, age, address):
        self.hello = '안녕하세요.'
        self.name = name
        self.age = age
        self.address = address
 
    def greeting(self):
        print('{0} 저는 {1}입니다.'.format(self.hello, self.name))
 
maria = Person('마리아', 20, '서울시 서초구 반포동')
maria.greeting()    # 안녕하세요. 저는 마리아입니다.
 
print('이름:', maria.name)       # 마리아
print('나이:', maria.age)        # 20
print('주소:', maria.address)    # 서울시 서초구 반포동

실행 결과

안녕하세요. 저는 마리아입니다.
이름: 마리아
나이: 20
주소: 서울시 서초구 반포동

__init__ 메서드를 보면 self 다음에 name, age, address를 지정했습니다. 그리고 메서드 안에서는 self.name = name처럼 매개변수를 그대로 self에 넣어서 속성으로 만들었습니다.

    def __init__(self, name, age, address):
        self.hello = '안녕하세요.'
        self.name = name
        self.age = age
        self.address = address

greeting 메서드는 인사를 하고 이름을 출력하도록 수정했습니다. 물론 name 속성에 접근할 때는 self.name처럼 사용해야 합니다.

    def greeting(self):
        print('{0} 저는 {1}입니다.'.format(self.hello, self.name))

이제 Person( )(괄호) 안에 이름, 나이, 주소를 콤마로 구분해서 넣은 뒤에 변수에 할당합니다. 이렇게 하면 이름은 '마리아', 나이는 20, 주소는 '서울시 서초구 반포동'maria 인스턴스가 만들어집니다.

maria = Person('마리아', 20, '서울시 서초구 반포동')

즉, 다음과 같이 Person의 괄호 안에 넣은 값은 __init__ 메서드에서 self 뒤에 있는 매개변수에 차례대로 들어갑니다.

그림 34-5 클래스로 인스턴스를 만들 때 값 받기

maria 인스턴스의 greeting 메서드를 호출해보면 '안녕하세요. 저는 마리아입니다.'처럼 인삿말과 함께 이름도 출력됩니다.

maria.greeting()    # 안녕하세요. 저는 마리아입니다.

클래스 안에서 속성에 접근할 때는 self.속성 형식이었죠? 클래스 바깥에서 속성에 접근할 때는 인스턴스.속성 형식으로 접근합니다. 다음과 같이 maria.name, maria.age, maria.address의 값을 출력해보면 Person으로 인스턴스를 만들 때 넣었던 값이 출력됩니다.

print('이름:', maria.name)       # 마리아
print('나이:', maria.age)        # 20
print('주소:', maria.address)    # 서울시 서초구 반포동

이렇게 인스턴스를 통해 접근하는 속성을 인스턴스 속성이라 부릅니다.

참고 | 클래스의 위치 인수, 키워드 인수

클래스로 인스턴스를 만들 때 위치 인수와 키워드 인수를 사용할 수 있습니다. 규칙은 함수와 같습니다. 위치 인수와 리스트 언패킹을 사용하려면 다음과 같이 *args를 사용하면 됩니다. 이때 매개변수에서 값을 가져오려면 args[0]처럼 사용해야 합니다.

class Person:
    def __init__(self, *args):
        self.name = args[0]
        self.age = args[1]
        self.address = args[2]
 
maria = Person(*['마리아', 20, '서울시 서초구 반포동'])

키워드 인수와 딕셔너리 언패킹을 사용하려면 다음과 같이 **kwargs를 사용하면 됩니다. 이때 매개변수에서 값을 가져오려면 kwargs['name']처럼 사용해야 합니다.

class Person:
    def __init__(self, **kwargs):    # 키워드 인수
        self.name = kwargs['name']
        self.age = kwargs['age']
        self.address = kwargs['address']
 
maria1 = Person(name='마리아', age=20, address='서울시 서초구 반포동')
maria2 = Person(**{'name': '마리아', 'age': 20, 'address': '서울시 서초구 반포동'})
참고 | 인스턴스를 생성한 뒤에 속성 추가하기, 특정 속성만 허용하기

지금까지 클래스의 인스턴스 속성은 __init__ 메서드에서 추가한 뒤 사용했습니다. 하지만 클래스로 인스턴스를 만든 뒤에도 인스턴스.속성 = 값 형식으로 속성을 계속 추가할 수 있습니다. 다음 Person 클래스는 빈 클래스이지만 인스턴스를 만든 뒤 name 속성을 추가합니다.

>>> class Person:
...     pass
...
>>> maria = Person()         # 인스턴스 생성
>>> maria.name = '마리아'    # 인스턴스를 만든 뒤 속성 추가
>>> maria.name
'마리아'

이렇게 추가한 속성은 해당 인스턴스에만 생성됩니다. 따라서 클래스로 다른 인스턴스를 만들었을 때는 추가한 속성이 생성되지 않습니다.

>>> james = Person()    # james 인스턴스 생성
>>> james.name    # maria 인스턴스에만 name 속성을 추가했으므로 james 인스턴스에는 name 속성이 없음
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    james.name
AttributeError: 'Person' object has no attribute 'name'

인스턴스는 생성한 뒤에 속성을 추가할 수 있으므로 __init__ 메서드가 아닌 다른 메서드에서도 속성을 추가할 수 있습니다. 단, 이때는 메서드를 호출해야 속성이 생성됩니다.

>>> class Person:
...     def greeting(self):
...         self.hello = '안녕하세요'    # greeting 메서드에서 hello 속성 추가
...
>>> maria = Person()
>>> maria.hello    # 아직 hello 속성이 없음
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    maria.hello
AttributeError: 'Person' object has no attribute 'hello'
>>> maria.greeting()    # greeting 메서드를 호출해야
>>> maria.hello         # hello 속성이 생성됨
'안녕하세요'

인스턴스는 자유롭게 속성을 추가할 수 있지만 특정 속성만 허용하고 다른 속성은 제한하고 싶을 수도 있습니다. 이때는 클래스에서 __slots__에 허용할 속성 이름을 리스트로 넣어주면 됩니다. 특히 속성 이름은 반드시 문자열로 지정해줍니다.

__slots__ = ['속성이름1, '속성이름2']

>>> class Person:
...     __slots__ = ['name', 'age']    # name, age만 허용(다른 속성은 생성 제한)
...
>>> maria = Person()
>>> maria.name = '마리아'                     # 허용된 속성
>>> maria.age = 20                            # 허용된 속성
>>> maria.address = '서울시 서초구 반포동'    # 허용되지 않은 속성은 추가할 때 에러가 발생함
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    maria.address = '서울시 서초구 반포동'
AttributeError: 'Person' object has no attribute 'address'