Python | 클래스 정의하기

Sua·2020년 12월 27일
0

Python

목록 보기
22/28
post-thumbnail

이제 데이터 유형을 클래스로 직접 정의해 볼 차례다. 클래스를 정의한 뒤에는 그 클래스에 속하는 인스턴스로 생성해 볼 것이다. 클래스·인스턴스의 이름공간과 그 속의 속성에 대해서도 알아본다.

class 문

class 클래스이름:    # ❶ 첫 행
    """독스트링"""   # ❷ 클래스의 의미와 역할 설명
    본문             # ❸ 클래스 공용 속성 정의

클래스의 이름을 지을 때는 파스칼 표기법을 따르는 것이 관례다.

파스칼 표기법(PascalCase): PythonProgramming처럼 단어와 단어를 대문자로 구별하는 방법이다. 파이썬 프로그래밍에서 클래스 이름을 지을 때 사용한다.
뱀 표기법(snake_case): python_programming처럼 단어와 단어를 밑줄 기호로 구별하는 방법이다. 파이썬 프로그래밍에서 변수·함수의 이름을 지을 때 사용한다.
낙타 표기법(camelCase): pythonProgamming처럼 대문자로 단어를 구별하되 첫 단어는 소문자로 쓰는 방법이다. 파이썬 프로그래밍에서는 잘 사용되지 않는다.

>>> class Cake:                       # ❶ 첫 행
...     """케익을 나타내는 클래스"""
...     coat = '생크림'               # ❷ 이 클래스의 공용 속성
... 
>>> Cake                              # ❸ 정의한 클래스 확인하기
<class '__main__.Cake'>

내가 정의한 클래스도 다른 클래스처럼 인스턴스화를 할 수 있다.
Cake 클래스의 인스턴스를 생성하고, type() 함수와 isinstance() 함수로 데이터 유형을 확인해보자.

>>> cake_1 = Cake()           # ❶ Cake의 인스턴스 만들기
>>> cake_2 = Cake()           # ❷ 인스턴스를 여러 개 만들 수 있다
>>> type(cake_1)              # ❸ cake_1의 데이터 유형 확인
<class '__main__.Cake'>

>>> isinstance(cake_2, Cake)  # ❹ cake_2가 Cake의 인스턴스인지 확인
True

속성이란?

속성은 일반적인 변수와 같은 것(즉, 이름이 붙은 데이터)인데, 클래스와 인스턴스 속에 정의된다는 것이 특징이다. 클래스의 속성은 그 클래스와 인스턴스 전체가 공유하는 특성을 나타낸다. 인스턴스의 속성은 개별 인스턴스의 고유한 특성을 나타낸다.

클래스 공용 속성

클래스의 속성은 클래스 객체 뿐 아니라 그 클래스의 모든 인스턴스가 공유한다.

>>> Cake.coat    # Cake 클래스의 coat 속성 읽기
'생크림'

>>> cake_1.coat  # cake_1 객체의 coat 속성 읽기
'생크림'

>>> cake_2.coat  # cake_2 객체의 coat 속성 읽기
'생크림'

클래스를 정의한 후에 속성을 수정하는 것도 가능하다. 케익 클래스의 속성을 수정해보자.

>>> Cake.coat = '초콜릿'  # 기존 속성 수정하기
>>> Cake.coat             # 수정한 속성 확인하기
'초콜릿'
>>> cake_1.coat      # 인스턴스의 속성 확인하기
'초콜릿'

>>> Cake.price = 4000     # 새 속성 추가하기
>>> Cake.price            # 추가한 속성 확인하기
4000

클래스의 속성은 모든 인스턴스가 공유하기 때문에, 클래스의 속성을 수정하면 인스턴스에서도 수정된 데이터를 읽을 수 있다.

일반적으로 클래스의 속성은 class 문에서 확정하고, 나중에는 수정하지 않는다.

인스턴스 전용 속성

인스턴스는 자신만의 속성을 가질 수 있다. 인스턴스의 속성은 클래스나 다른 인스턴스와 공유되지 않는다.

>>> cake_1.topping = '블루베리'  # ❶ 인스턴스에 topping 속성 추가하기
>>> cake_1.topping               # ❷ 인스턴스에 속성이 추가되었다
'블루베리'

>>> Cake.topping                 # ❸ 클래스에는 새로 추가한 속성이 없다
AttributeError: type object 'Cake' has no attribute 'topping'

>>> cake_2.topping               # ❹ 다른 인스턴스에도 새로 추가한 속성이 없다
AttributeError: 'Cake' object has no attribute 'topping'

❶ cake_1 객체에 topping 속성을 추가했다. ❷ 이 속성은 cake_1 객체에서만 포함되어 있고, ❸ Cake 클래스나 ❹ 다른 객체에는 포함되어 있지 않다.

이번에는 클래스에 정의해 놓은 공용 속성을 인스턴스에서 수정해보자.

>>> cake_1.coat                 # ❶ Cake 클래스의 coat 속성을 가리키고 있다
'생크림'

>>> cake_1.coat = '아이스크림'  # ❷ cake_1 객체에 coat 속성을 추가한다

>>> cake_1.coat                 # ❸ cake_1은 이제 자신의 고유한 coat 속성을 갖고 있다
'아이스크림'
   
>>> Cake.coat                   # ❹ Cake 클래스는 자신의 coat 속성을 유지하고 있다
'생크림'

>>> cake_2.coat                 # ❺ 다른 객체는 계속 클래스의 coat 속성을 공유하고 있다
'생크림'

❷ 인스턴스 속성을 대입하면 cake_1 객체에 자신만의 인스턴스 속성 coat가 추가되며, 기존의 공용 속성은 가려진다.

클래스의 속성을 수정한 게 아니라는 점을 주의하자. ❸ cake_1 객체는 자신만의 coat 속성을 갖지만, ❹ Cake 클래스와 ❺ 다른 인스턴스는 여전히 클래스 속성을 공유한다.

이름공간이란?

이름공간은 프로그래밍 언어에서 이름이 가리키는 대상을 제한하는 범위다.

같은 이름이라도 그 문맥이 클래스냐 인스턴스냐에 따라 다음과 같이 다른 의미를 내포할 것이다.

coat는 Cake 클래스의 이름공간에서는 일반적인 케익의 코팅을 의미한다. 반면, cake_1 객체의 이름공간에서는 그 케익만의 고유한 코팅을 의미한다.

dir() 함수로 이름공간에 정의된 이름 구하기

dir() 함수를 사용하면 이름공간(클래스)에 정의된 모든 이름(속성)의 리스트를 구할 수 있다.

파이썬의 기본 데이터 유형이나 다른 사람이 정의한 클래스에 어떤 속성이 있는지 확인하고 싶을 때 dir() 함수를 활용하자.

메서드란?

클래스와 인스턴스의 이름공간에는 다양한 데이터를 속성으로 정의할 수 있다. 함수도 데이터이므로 속성이 될 수 있다.

클래스나 인스턴스에 속한 함수는 그 데이터 종류를 위한 전용 함수로 기능하게 된다. 이 함수를 메서드(method)라고 부른다. 영어 단어 ‘method’는 ‘방법’이라는 뜻이다. 즉, 메서드는 데이터 유형을 다루는 방법이 정의된 함수다.

메서드 정의하기

메서드는 속성이므로, 클래스 공용 또는 특정 인스턴스 전용으로 정의할 수 있다. 하지만 대부분의 메서드는 클래스 공용 속성으로 정의한다. 데이터 유형을 다루는 방법은 데이터 유형에 따라 정하기 마련이기 때문이다.

class 문 안에서 def 문으로 함수를 정의하면 된다. Cake 클래스에 메서드를 추가해 새로 정의해 보자.

class Cake:
    """케익을 나타내는 클래스"""
    coat = '생크림'
  
    def describe():                           # ❷ 메서드 정의하기
        """이 케익에 관한 정보를 화면에 출력한다."""
        print('이 케익은', Cake.coat, '으로 덮여 있다.')  

메서드는 별도의 이름공간을 가지기 때문에 본문에서 속성을 가리키지 못한다. coat이라고만 쓰면 이름 오류가 발생하므로, Cake.coat와 같이 속성을 가진 클래스의 이름을 붙여야 한다.

Cake.describe()
이 케익은 생크림 으로 덮여 있다.

하지만, 이 방법으로는 인스턴스 전용 속성을 출력하지는 못할 것이다. ‘이 케익의 토핑’을 출력하고 싶은데 ‘케익 일반의 토핑’만 출력하는 메서드가 된 것이다. 어떻게 메서드에서 인스턴스의 속성을 읽을 수 있을까?

인스턴스를 위한 메서드

class Cake:
    """케익을 나타내는 클래스"""
    coat = '생크림'
    
    def describe(self):                                    # ❶
        """이 케익에 관한 정보를 화면에 출력한다."""
        print('이 케익은', self.coat, '으로 덮여 있다.')   # ❷

❶ describe() 메서드에 인스턴스를 전달받기 위한 매개변수 self를 추가했다. ❷ 클래스의 속성 Cake.coat 대신 인스턴스의 속성 self.coat를 화면에 출력하도록 변경했다. ‘self’라는 이름은 ‘자신’을 의미하는 영어 단어에서 딴 것이다. 파이썬에서 인스턴스를 전달받는 메서드의 매개변수 이름으로 이 이름을 사용하는 것이 관례다.

메서드는 클래스보다는 개별 인스턴스를 조작하기 위한 것일 때가 많다. 그래서 대부분의 메서드는 self 매개변수를 첫 번째 매개변수로 갖는다.

class 클래스이름:
    """독스트링"""
    클래스 공용 속성
    
    def 메서드(self, ...):
        """이 클래스의 인스턴스를 self 매개변수에 전달받아 처리하는 함수"""
        메서드 본문
    
    ...(필요한 만큼 메서드를 추가 정의)

단, 인스턴스를 전달받도록 메서드를 정의해 두면, 클래스를 기준으로 메서드를 호출했을 때 오류가 발생한다.

메서드는 여러 인스턴스가 공유하기 때문에 클래스의 속성으로 정의한다. 그런데 메서드는 클래스가 아니라 인스턴스를 기준으로 실행할 때가 많다. 정의하는 곳과 사용하는 곳이 다르다. 헷갈리지 않도록 주의하자.

  • 메서드는 클래스와 인스턴스의 속성으로 정의된 함수이며, 특정 데이터 유형을 다루는 방법을 나타낸다.
  • 메서드는 대부분 class 문 내부에서 클래스 공용 속성으로 정의된다.
  • 메서드는 클래스 또는 인스턴스를 기준으로 호출할 수 있다. 인스턴스를 기준으로 호출할 때는 인스턴스가 메서드에 전달된다. 이를 통해 메서드에서 인스턴스 전용 속성에 접근할 수 있다.

__init__() 메서드로 인스턴스 초기화하기

인스턴스를 생성하면 가장 먼저 인스턴스 속성을 정의해야 한다. 이것을 초기화(initialization)라고 한다. 그런데 인스턴스를 만들 때마다 속성을 직접 대입하는 것은 불편하다. 좀 더 편리하게 인스턴스를 초기화하는 방법을 알아보자.

cake_1 = Cake()
cake_2 = Cake()
cake_3 = Cake()
cake_1.candles = 0
cake_2.candles = 0
cake_3.candles = 0

__init__()는 새로 만들어진 객체의 속성을 초기화하는 메서드다. 기본 제공되는 __init__() 메서드는 객체에 아무런 속성도 부여하지 않지만, 이 메서드를 직접 정의하면 인스턴스의 초기화 방법을 지시할 수 있다.

class Cake:
    """케익을 나타내는 클래스"""
    coat = '생크림'
    
    def __init__(self, candles):                          # ❶
        """인스턴스를 초기화한다."""
        self.candles = candles
    
    def describe(self):
        """이 케익에 관한 정보를 화면에 출력한다."""
        print('이 케익은', self.coat, '으로 덮여 있다.')
        print('초가', self.candles, '개 꽂혀 있다.')      # ❷

❶ __init__ 메서드를 새로 추가했다. 매개변수로는 생성된 인스턴스를 전달받을 self와 초기값으로 지정할 값을 전달받을 candles를 정의했다.

cake_1 = Cake(12)   # 이제 초기값을 지정하여
cake_2 = Cake(100)  # 인스턴스화할 수 있다

print('케익 1:')
print('초 개수:', cake_1.candles)
12

print('케익 2:')
cake_2.describe()
케익 1:
초 개수: 12
케익 2:
이 케익은 생크림 으로 덮여 있다.
초가 100 개 꽂혀 있다.

인스턴스화를 수행할 때 Cake(12)와 같이 초기화에 필요한 데이터를 괄호 안에 넣어 인자로 전달하면 된다. __init__() 메서드는 첫번째 매개변수에 인스턴스를 전달받고, 두번째 매개변수부터 사용자가 전달한 인자를 입력받아 초기화에 사용한다.

__init__() 메서드를 잘 정의해두면 인스턴스화 과정에서 초기화가 함께 수행되도록 할 수 있다. 대부분의 객체는 초기화가 필요하므로, 클래스를 정의할 때 __init__() 메서드를 함께 정의해야 할 때가 많다. 그러므로 클래스 정의 양식을 다음과 같이 확장해 기억하자.

class 클래스이름:
    """독스트링"""
    클래스 공용 속성
    
    def __init__(self, ...):
        """인스턴스를 초기화한다."""
        인스턴스 전용 속성 초기화
    
    def 메서드(self, ...):
        """이 클래스의 인스턴스를 self 매개변수에 전달받아 처리하는 함수"""
        메서드 본문
    
    ...(필요한 만큼 메서드를 추가 정의)

인스턴스가 가져야 할 속성

클래스 공용 속성으로는 메서드와 그 범주의 일반적 속성을 정의하고, 인스턴스 전용 속성으로는 개별 객체의 고유한 속성을 정의해야 한다. __init__() 메서드는 인스턴스가 가져야 할 속성을 정하는 역할을 한다.

class Cake:
    """케익을 나타내는 클래스"""
    coat = '생크림'
    
    def __init__(self, topping, price, candles=0):
        """인스턴스를 초기화한다."""
        self.topping = toping   # 케익에 올린 토핑
        self.price = price      # 케익의 가격
        self.candles = candles  # 케익에 꽂은 초 개수
    
    def describe(self):
        """이 케익에 관한 정보를 화면에 출력한다."""
        print('이 케익은', self.coat, '으로 덮여 있다.')
        print(self.topping, '을 올려 장식했다.')
        print('가격은', self.price, '원이다.')
        print('초가', self.candles, '개 꽂혀 있다.')

새 Cake 클래스는 개별 케익 객체가 가져야 할 정보를 __init__() 메서드가 전달받아 초기화한다. __init__ 메서드를 보면 인스턴스 속성으로 topping, price, candles가 있다는 것을 알 수 있다. 케익이 처음 나올 때는 초를 꽂아두지 않을 것이므로, cendles 매개변수는 기본값을 0으로 지정해 두었다. 이제 Cake 클래스를 인스턴스화할 때 케익의 속성을 모두 지정할 수 있다.

객체를 생성할 때 __init__ 메서드에 필요한 값을 충분히 제공하지 않으면 오류가 발생한다. 초기화에 필요한 정보가 모자라면 인스턴스의 생성이 이루어지지 않는 셈이다. 인스턴스에 필요한 속성을 모두 __init__ 메서드에 정의해두면, 속성이 누락된 객체가 만들어지지 않게 방지할 수 있다.

  • 객체를 만들 때 객체의 초기 속성을 정해주는 것을 초기화라고 한다.
  • __init__() 메서드를 정의하여 인스턴스를 자동으로 초기화할 수 있다.
  • 인스턴스에서 사용할 모든 속성을 __init__() 메서드에서 정의하자.

본 포스팅은 아래의 사이트를 참고하여 작성되었습니다.
연오의 파이썬 https://python.bakyeono.net/chapter-6.html

profile
Leave your comfort zone

0개의 댓글