36.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에 점을 붙여서 사용하면 됩니다.

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

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

그림 36-4 인스턴스와 self
그림 36 4 인스턴스와 self

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

이번에는 클래스로 인스턴스를 만들 때 값을 지정하는 방법을 알아보겠습니다. 다음과 같이 __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 뒤에 있는 매개변수에 차례대로 들어갑니다.

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

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

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

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

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

지금까지 사용한 속성은 인스턴스를 통해 접근했습니다. 그래서 이러한 속성을 인스턴스 속성이라 부릅니다.

참고 | 클래스의 멤버

클래스에 들어있는 속성과 메서드를 멤버(member)라고 부릅니다. 여기서는 hello, name, age, addressgreetingPerson 클래스의 멤버입니다.

지금까지 사용한 int, list, dict 등도 사실 클래스입니다. 우리는 이 클래스로 인스턴스를 만들고 메서드를 사용했습니다.

>>> a = int(10)
>>> a
10
>>> b = list(range(10))
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c = dict(x=10, y=20)
>>> c
{'x': 10, 'y': 20}

int 클래스에 10을 넣어서 인스턴스 a를 만들었습니다. 마찬가지로 list 클래스에 range(10)을 넣어서 인스턴스 b를 만들고, dict 클래스에 x=10, y=20을 넣어서 인스턴스 c를 만들었습니다. 잘 보면 Person으로 인스턴스를 만드는 방법과 똑같습니다.

물론 정수는 매우 자주 사용하므로 int를 생략하고 10을 바로 넣습니다. 그리고 리스트와 딕셔너리도 자주 사용하므로 축약된 문법인 [ ]{ }를 제공하지만 클래스인 것은 같습니다.

다음과 같이 리스트를 조작할 때 메서드를 사용했었죠? 인스턴스 b에서 메서드 append를 호출해서 값을 추가합니다. 이 부분도 지금까지 메서드를 만들고 사용한 것과 같은 방식입니다.

>>> b = list(range(10))
>>> b.append(20)
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20]

즉, 파이썬에서는 자료형도 클래스입니다. 다음과 같이 type을 사용하면 객체(인스턴스)가 어떤 클래스인지 확인할 수 있습니다.

  • type(객체)
  • >>> a = 10
    >>> type(a)
    <class 'int'>
    >>> b = [0, 1, 2]
    >>> type(b)
    <class 'list'>
    >>> c = {'x':10, 'y':20}
    >>> type(c)
    <class 'dict'>
    >>> maria = Person()
    >>> type(maria)
    <class '__main__.Person'>
    
    참고 | 인스턴스와 객체

    어떨 때는 인스턴스라고 했다가 어떨 때는 객체라고 하니 조금 헷갈리죠? 사실 인스턴스와 객체는 같은 것을 뜻합니다. 보통 객체만 지칭할 때는 그냥 객체라고 부릅니다. 하지만 클래스와 연관지어서 말할 때는 인스턴스라고 부릅니다. 그래서 다음과 같이 리스트 변수 a, b가 있으면 a, b는 객체입니다. 그리고 ablist 클래스의 인스턴스입니다.

    >>> a = list(range(10))
    >>> b = list(range(20))
    
    참고 | 클래스의 위치 인수, 키워드 인수

    클래스로 인스턴스를 만들 때 위치 인수와 키워드 인수를 사용할 수 있습니다. 규칙은 함수와 같습니다. 위치 인수와 리스트 언패킹을 사용하려면 다음과 같이 *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 속성이 생성됨
    '안녕하세요'
    

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

    __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'