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 메서드에서는 print로 self.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.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 뒤에 있는 매개변수에 차례대로 들어갑니다.
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'