23. 클래스(Class)
1. 키워드
- 객체지향 프로그래밍(Object Oriented Programming)
- 클래스(Class)
- 객체(Object)
- 인스턴스(Instance)
- 속성(Attribute)
- 메서드(Method)
- 스페셜 메서드(Special Method) 또는 매직 메서드(Magic Method)
- 비공개 속성(Private Attribute)과 비공개 메서드(Private Method)
2. 클래스 사용하기
- 클래스는 객체를 표현하기 위한 문법이다.
- 예를 들어 게임을 만든다고 하면
기사
,마법사
,궁수
,사제
등 직업별로 클래스를 만들어서 표현할 수 있다. 기사
,마법사
,궁수
,사제
등과 같이 특정한 개념이나 모양으로 존재하는 것을 객체라고 부른다.- 그리고 프로그래밍으로 객체를 만들 때 사용하는 것이 클래스이다.
- 예를 들어 게임 캐릭터는
체력
,마나
,물리 공격력
,주문력
등이 필요하다. - 그리고 기사 캐릭터는 칼로
베기
,찌르기
등의 스킬이 있어야 한다. - 여기서
체력
,마나
,물리 공격력
,주문력
등의 데이터를 클래스의 속성이라고 부르고,베기
,찌르기
등의 기능을 메서드라고 부른다.
- 이렇게 프로그래밍하는 방법을 객체지향 프로그래밍이라고 한다.
- 객체지향 프로그래밍은 복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합해서 문제를 해결한다.
- 따라서 현실 세계의 복잡한 문제를 처리하는데 유용하며 기능을 개선하고 발전시킬 때도 해당 클래스만 수정하면 되므로 유지 보수에도 효율적이다.
- 지금까지 숫자
1
,2
,3
, 문자"a"
,"b"
,"c"
, 리스트, 딕셔너리 등을 조합해서 프로그램을 만들었는데 사실 파이썬에서는 이 모든 것이 객체이다.
3. 클래스와 메서드 만들기
- 클래스는
class
에 클래스 이름을 지정하고:
(콜론)을 붙인 뒤 다음 줄부터def
로 메서드를 작성하면 된다. - 여기서 메서드는 클래스 안에 들어있는 함수를 뜻한다.
- 클래스 이름을 짓는 방법은 변수와 같다.
- 보통 파이썬에서는 클래스의 이름은 대문자로 시작한다.
- 그리고 메서드 작성 방법은 함수와 같으며 코드는 반드시 들여쓰기를 해야 한다.
- 특히 메서드의 첫 번째 매개변수는 반드시
self
를 지정해야 한다.
- 이제 간단한 사람 클래스를 작성해 보자.
- 다음과 같이 클래스에
()
(괄호)를 붙인 뒤 변수에 할당한다.
Person
으로 변수james
를 만들었는데 이james
가Person
의 인스턴스이다.- 클래스는 특정 개념을 표현만 할 뿐 사용을 하려면 인스턴스를 생성해야 한다.
1) 메서드 호출하기
- 메서드는 클래스가 아니라 인스턴스를 통해 호출한다.
- 다음과 같이 인스턴스 뒤에
.
(점)을 붙이고 메서드를 호출하면 된다.
james.greeting
을 호출하니"Hello"
가 출력되었다.- 이렇게 인스턴스를 통해 호출하는 메서드를 인스턴스 메서드라고 부른다.
2) 파이썬에서 흔히 볼 수 있는 클래스
- 지금까지 사용한
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
를 만들었다.
- 다음과 같이 리스트를 조작하는 부분도 지금까지 메서드를 만들고 사용한 것과 같은 방식이다.
- 즉, 파이썬에서는 자료형도 클래스이다.
- 다음과 같이
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'>
3) 인스턴스와 객체의 차이점?
- 보통 객체만 지칭할 때는 그냥 객체라고 부른다.
- 하지만 클래스와 연관지어서 말할 때는 인스턴스라고 부른다.
- 그래서 다음과 같이 리스트 변수
a
,b
가 있을 때 "a
,b
는 객체입니다." 그리고 "a
와b
는list
클래스의 인스턴스입니다."라고 할 수 있다.
메소드 안에서 메서드 호출하기
- 메서드 안에서 메서드를 호출할 때는 다음과 같이
self.메서드()
형식으로 호출해야 한다. self
없이 메서드 이름만 사용하면 클래스 바깥쪽에 있는 함수를 호출한다는 뜻이 되므로 주의해야 한다.
특정 클래스의 인스턴스인지 확인하기
- 현재 인스턴스가 특정 클래스의 인스턴스인지 확인할 때는
isinstance
함수를 사용한다. - 특정 클래스의 인스턴스가 맞으면
True
, 아니면False
를 반환한다.
isinstance
는 주로 객체의 자료형을 판단할 때 사용한다.- 예를 들어 팩토리얼 함수는
1
부터n
까지 양의 정수를 차례대로 곱해야 하는데, 실수와 음의 정수는 계산할 수 없다. - 이런 경우에
isinstance
를 사용하여 숫자(객체)가 정수일 때만 계산하도록 만들 수 있다.
4. 속성 사용하기
- 속성을 만들 때는
__init__
메서드 안에서self.속성
에 값을 할당한다.
class Person:
def __init__(self):
self.hello = "안녕하세요."
def greeting(self):
print(self.hello)
james = Person()
james.greeting() # 안녕하세요.
Person
클래스의__init__
메서드에서self.hello
에"안녕하세요."
인사말을 넣었다.
__init__
메서드는james = Person()
처럼 클래스에()
(괄호)를 붙여서 인스턴스를 만들 때 호출되는 특별한 메서드이다.- 즉,
__init__
(Initialize)이라는 이름 그대로 인스턴스(객체)를 초기화한다. - 특히 이렇게 앞 뒤로
__
(밑줄 두 개)가 붙은 메서드는 파이썬이 자동으로 호출해 주는 메서드인데 스페셜 메서드 또는 매직 메서드라고 부른다.
greeting
메서드에서는print
로self.hello
를 출력하도록 만들었다.
- 그다음에
Person
클래스로 인스턴스를 만들고,greeting
메서드를 호출해 보면self.hello
에 저장된"안녕하세요."
가 출력된다.
- 속성은
__init__
메서드에서 만든다는 점과self
에.
(점)을 붙인 뒤 값을 할당한다는 점이 중요하다. - 클래스 안에서 속성을 사용할 때도
self.hello
처럼self
에 점을 붙여서 사용하면 된다.
1) self
의 의미
self
는 인스턴스 자기자신을 의미한다.- 인스턴스가 생성될 때
self.hello = "안녕하세요."
처럼 자기자신에 속성을 추가했다. - 여기서
__init__
의 매개변수self
에 들어가는 값은Person()
이라고 할 수 있고,self
가 완성된 뒤james
에 할당된다. - 이후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수
self
에 들어온다. - 그래서
greeting
메서드에서print(self.hello)
처럼 속성을 출력할 수 있었던 것이다.
- 인스턴스와
self
의 관계를 그림으로 나타내면 다음과 같은 모양이 된다.
2) 인스턴스를 만들 때 값 받기
- 다음과 같이
__init__
메서드에서self
다음에 값을 받을 매개변수를 지정하고 매개변수를self.속성
에 넣어준다.
Person
클래스로 인스턴스를 만들 때name
,age
,address
를 받아보자.
class Person:
def __init__(self, name, age, address):
self.hello = "안녕하세요"
self.name = name
self.age = age
self.address = address
def greeting(self):
print(f"{self.hello} 저는 {self.name}입니다.")
maria = Person("마리아", 20, "서울시 서초구 반포동")
maria.greeting() # 안녕하세요. 저는 마리아입니다.
print("이름: ", maria.name) # 이름: 마리아
print("나이: ", maria.age) # 나이: 20
print("주소: ", maria.address) # 주소: 서울시 서초구 반포동
__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
메서드는 인사를 하고 이름을 출력하도록 수정했다.
- 이제
Person
의()
(괄호) 안에name
,age
,address
를 콤마로 구분해서 넣은 뒤에 변수에 할당한다.
- 이렇게 하면
name
은"마리아"
,age
는20
,address
는"서울시 서초구 반포동"
인maria
인스턴스가 만들어진다.
- 즉, 다음과 같이
Person
의 괄호 안에 넣은 값은__init__
메서드에서self
뒤에 있는 매개변수에 차례대로 들어간다.
maria
인스턴스의greeting
메서드를 호출해 보면"안녕하세요. 저는 마리아입니다."
처럼 인삿말과 함께 이름도 출력된다.
- 클래스 바깥에서 속성에 접근할 때는
인스턴스.속성
형식으로 접근한다.
- 다음과 같이
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"]
처럼 사용해야 한다.
인스턴스를 생성한 뒤에 속성 추가하기, 특정 속성만 허용하기
- 지금까지 클래스의 인스턴스 속성은
__init__
메서드에서 추가한 뒤 사용했다. - 하지만 클래스로 인스턴스를 만든 뒤에도
인스턴스.속성 = 값
형식으로 속성을 계속 추가할 수 있다.
- 다음
Person
클래스는 빈 클래스이지만 인스턴스를 만든 뒤name
속성을 추가한다.
- 이렇게 추가한 속성은 해당 인스턴스에만 생성되기 때문에, 클래스로 다른 인스턴스를 만들었을 때는 추가한 속성이 생성되지 않는다.
- 인스턴스는 생성한 뒤에 속성을 추가할 수 있으므로
__init__
메서드가 아닌 다른 메서드에서도 속성을 추가할 수 있다. - 단, 이때는 메서드를 호출해야 속성이 생성된다.
>>> class Person:
... def greeting(self):
... self.hello = "안녕하세요"
...
>>> maria = Person()
>>> maria.hello # AttributeError: 'Person' object has no attribute 'hello'
>>> maria.greeting()
>>> maria.hello
'안녕하세요'
- 인스턴스는 자유롭게 속성을 추가할 수 있지만 특정 속성만 허용하고 다른 속성은 제한하고 싶을 수도 있다.
- 이때는 클래스에서
__slots__
에 허용할 속성 이름을 리스트로 넣어주면 된다. - 특히 속성 이름은 반드시 문자열로 지정해 준다.
5. 비공개 속성 사용하기
- 앞에서 만든
Person
클래스에는hello
,name
,age
,address
속성이 있었다.
class Person:
def __init__(self, name, age, address):
self.hello = "안녕하세요."
self.name = name
self.age = age
self.address = address
- 이 속성들은 메서드에서
self
로 접근할 수 있고,인스턴스.속성
형식으로 클래스 바깥에서도 접근할 수 있다.
- 이번에는 클래스 바깥에서는 접근할 수 없고 클래스 안에서만 사용할 수 있는 비공개 속성을 사용해 보자.
- 비공개 속성은
__속성
과 같이 이름이__
(밑줄 두 개)로 시작해야 한다.
- 단,
__속성__
처럼 밑줄 두 개가 양 옆에 왔을 때는 비공개 속성이 아니므로 주의해야 한다.
Person
클래스에 지갑 속성__wallet
을 넣어보자.
class Person:
def __init__(self, name, age, address, wallet):
self.name = name
self.age = age
self.address = address
self.__wallet = wallet
maria = Person("마리아", 20, "서울시 서초구 반포동", 10000)
maria.__wallet -= 10000 # AttributeError: 'Person' object has no attribute '__wallet'
self.__wallet
처럼 앞에 밑줄 두 개를 붙여서 비공개 속성으로 만들었으므로 클래스 바깥에서maria.__wallet
으로는 접근할 수 없다.- 비공개 속성은 클래스 안의 메서드에서만 접근할 수 있다.
- 다음과 같이 돈을 내는
pay
메서드를 만들어보자.
class Person:
def __init__(self, name, age, address, wallet):
self.name = name
self.age = age
self.address = address
self.__wallet = wallet
def pay(self, amount):
self.__wallet -= amount
print(f"이제 {self.__wallet}원 남았네요.")
maria = Person("마리아", 20, "서울시 서초구 반포동", 10000)
maria.pay(3000) # 이제 7000원 남았네요.
- 이처럼 비공개 속성은 클래스 바깥으로 드러내고 싶지 않은 값에 사용한다.
- 즉, 중요한 값인데 바깥에서 함부로 바꾸면 안 될 때 비공개 속성을 주로 사용한다.
- 비공개 속성을 바꾸는 경우는 클래스의 메서드로 한정한다.
공개 속성과 비공개 속성
- 클래스 바깥에서 접근할 수 있는 속성을 공개 속성이라고 부르고, 클래스 안에서만 접근할 수 있는 속성을 비공개 속성이라고 부른다.
비공개 메서드 사용하기
- 속성뿐만 아니라 메서드도 이름이
__
(밑줄 두 개)로 시작하면 클래스 안에서만 호출할 수 있는 비공개 메서드가 된다.
class Person:
def __greeting(self):
print("Hello")
def hello(self):
self.__greeting()
james = Person()
james.__greeting() # AttributeError: 'Person' object has no attribute '__greeting'
- 비공개 메서드도 메서드를 클래스 바깥으로 드러내고 싶지 않을 때 사용한다.
- 보통 내부에서만 호출되어야 하는 메서드를 비공개 메서드로 만든다.