Skip to content

23. 클래스(Class)


1. 키워드

  • 객체지향 프로그래밍(Object Oriented Programming)
  • 클래스(Class)
  • 객체(Object)
  • 인스턴스(Instance)
  • 속성(Attribute)
  • 메서드(Method)
  • 스페셜 메서드(Special Method) 또는 매직 메서드(Magic Method)
  • 비공개 속성(Private Attribute)과 비공개 메서드(Private Method)


2. 클래스 사용하기

  • 클래스는 객체를 표현하기 위한 문법이다.
  • 예를 들어 게임을 만든다고 하면 기사, 마법사, 궁수, 사제 등 직업별로 클래스를 만들어서 표현할 수 있다.
  • 기사, 마법사, 궁수, 사제 등과 같이 특정한 개념이나 모양으로 존재하는 것을 객체라고 부른다.
  • 그리고 프로그래밍으로 객체를 만들 때 사용하는 것이 클래스이다.
  • 예를 들어 게임 캐릭터는 체력, 마나, 물리 공격력, 주문력 등이 필요하다.
  • 그리고 기사 캐릭터는 칼로 베기, 찌르기 등의 스킬이 있어야 한다.
  • 여기서 체력, 마나, 물리 공격력, 주문력 등의 데이터를 클래스의 속성이라고 부르고, 베기, 찌르기 등의 기능을 메서드라고 부른다.


001


  • 이렇게 프로그래밍하는 방법을 객체지향 프로그래밍이라고 한다.
  • 객체지향 프로그래밍은 복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합해서 문제를 해결한다.
  • 따라서 현실 세계의 복잡한 문제를 처리하는데 유용하며 기능을 개선하고 발전시킬 때도 해당 클래스만 수정하면 되므로 유지 보수에도 효율적이다.
  • 지금까지 숫자 1, 2, 3, 문자 "a", "b", "c", 리스트, 딕셔너리 등을 조합해서 프로그램을 만들었는데 사실 파이썬에서는 이 모든 것이 객체이다.


3. 클래스와 메서드 만들기

  • 클래스는 class에 클래스 이름을 지정하고 :(콜론)을 붙인 뒤 다음 줄부터 def로 메서드를 작성하면 된다.
  • 여기서 메서드는 클래스 안에 들어있는 함수를 뜻한다.
  • 클래스 이름을 짓는 방법은 변수와 같다.
  • 보통 파이썬에서는 클래스의 이름은 대문자로 시작한다.
  • 그리고 메서드 작성 방법은 함수와 같으며 코드는 반드시 들여쓰기를 해야 한다.
  • 특히 메서드의 첫 번째 매개변수는 반드시 self를 지정해야 한다.


class 클래스이름:
    def 메서드(self):
        코드


  • 이제 간단한 사람 클래스를 작성해 보자.


>>> class Person:
...     def greeting(self):
...         print("Hello")
...


  • 다음과 같이 클래스에 ()(괄호)를 붙인 뒤 변수에 할당한다.


인스턴스 = 클래스()
>>> james = Person()


  • Person으로 변수 james를 만들었는데 이 jamesPerson의 인스턴스이다.
  • 클래스는 특정 개념을 표현만 할 뿐 사용을 하려면 인스턴스를 생성해야 한다.


1) 메서드 호출하기

  • 메서드는 클래스가 아니라 인스턴스를 통해 호출한다.


  • 다음과 같이 인스턴스 뒤에 .(점)을 붙이고 메서드를 호출하면 된다.


인스턴스.메서드()
>>> james.greeting()
Hello


  • 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를 만들었다.


  • 다음과 같이 리스트를 조작하는 부분도 지금까지 메서드를 만들고 사용한 것과 같은 방식이다.


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


3) 인스턴스와 객체의 차이점?

  • 보통 객체만 지칭할 때는 그냥 객체라고 부른다.
  • 하지만 클래스와 연관지어서 말할 때는 인스턴스라고 부른다.
  • 그래서 다음과 같이 리스트 변수 a, b가 있을 때 "a, b는 객체입니다." 그리고 "ablist 클래스의 인스턴스입니다."라고 할 수 있다.


빈 클래스 만들기

  • 내용이 없는 빈 클래스를 만들 때는 코드 부분에 pass를 넣어준다.


class Person:
    pass


메소드 안에서 메서드 호출하기

  • 메서드 안에서 메서드를 호출할 때는 다음과 같이 self.메서드() 형식으로 호출해야 한다.
  • self 없이 메서드 이름만 사용하면 클래스 바깥쪽에 있는 함수를 호출한다는 뜻이 되므로 주의해야 한다.


class Person:
    def greeting(self):
        print("Hello")

    def hello(self):
        self.greeting()

james = Person()

james.hello() # Hello


특정 클래스의 인스턴스인지 확인하기

  • 현재 인스턴스가 특정 클래스의 인스턴스인지 확인할 때는 isinstance 함수를 사용한다.
  • 특정 클래스의 인스턴스가 맞으면 True, 아니면 False를 반환한다.


isinstance(인스턴스, 클래스)
>>> class Person:
...     pass
...

>>> james = Person()

>>> isinstance(james, Person)
True


  • isinstance는 주로 객체의 자료형을 판단할 때 사용한다.
  • 예를 들어 팩토리얼 함수는 1부터 n까지 양의 정수를 차례대로 곱해야 하는데, 실수와 음의 정수는 계산할 수 없다.
  • 이런 경우에 isinstance를 사용하여 숫자(객체)가 정수일 때만 계산하도록 만들 수 있다.


def factorial(n):
    if not isinstance(n, int) or n < 0:
        return None

    if n == 1:
        return 1

    return n * factorial(n - 1)


4. 속성 사용하기

  • 속성을 만들 때는 __init__ 메서드 안에서 self.속성에 값을 할당한다.


class 클래스이름:
    def __init__(self):
        self.속성 = 
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)이라는 이름 그대로 인스턴스(객체)를 초기화한다.
  • 특히 이렇게 앞 뒤로 __(밑줄 두 개)가 붙은 메서드는 파이썬이 자동으로 호출해 주는 메서드인데 스페셜 메서드 또는 매직 메서드라고 부른다.


  • greeting 메서드에서는 printself.hello를 출력하도록 만들었다.


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


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


james = Person()

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


  • 속성은 __init__ 메서드에서 만든다는 점과 self.(점)을 붙인 뒤 값을 할당한다는 점이 중요하다.
  • 클래스 안에서 속성을 사용할 때도 self.hello처럼 self에 점을 붙여서 사용하면 된다.


1) self의 의미

  • self는 인스턴스 자기자신을 의미한다.
  • 인스턴스가 생성될 때 self.hello = "안녕하세요."처럼 자기자신에 속성을 추가했다.
  • 여기서 __init__의 매개변수 self에 들어가는 값은 Person()이라고 할 수 있고, self가 완성된 뒤 james에 할당된다.
  • 이후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수 self에 들어온다.
  • 그래서 greeting 메서드에서 print(self.hello)처럼 속성을 출력할 수 있었던 것이다.


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


002


2) 인스턴스를 만들 때 값 받기

  • 다음과 같이 __init__ 메서드에서 self 다음에 값을 받을 매개변수를 지정하고 매개변수를 self.속성에 넣어준다.


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


  • 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 메서드는 인사를 하고 이름을 출력하도록 수정했다.


    def greeting(self):
        print(f"{self.hello} 저는 {self.name}입니다.")


  • 이제 Person()(괄호) 안에 name, age, address를 콤마로 구분해서 넣은 뒤에 변수에 할당한다.


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


  • 이렇게 하면 name"마리아", age20, address"서울시 서초구 반포동"maria 인스턴스가 만들어진다.


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


003


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


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"]처럼 사용해야 한다.


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.name # AttributeError: 'Person' object has no attribute '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__에 허용할 속성 이름을 리스트로 넣어주면 된다.
  • 특히 속성 이름은 반드시 문자열로 지정해 준다.


__slots__ = ["속성이름1", "속성이름2"]
>>> class Person:
...     __slots__ = ["name", "age"]
...

>>> maria = Person()

>>> maria.name = "마리아"
>>> maria.age = 20
>>> maria.address = "서울시 서초구 반포동" # AttributeError: 'Person' object has no attribute 'address'


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로 접근할 수 있고, 인스턴스.속성 형식으로 클래스 바깥에서도 접근할 수 있다.


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

>>> maria.name
'마리아'


  • 이번에는 클래스 바깥에서는 접근할 수 없고 클래스 안에서만 사용할 수 있는 비공개 속성을 사용해 보자.


  • 비공개 속성은 __속성과 같이 이름이 __(밑줄 두 개)로 시작해야 한다.


class 클래스이름:
    def __init__(self, 매개변수):
        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'


  • 비공개 메서드도 메서드를 클래스 바깥으로 드러내고 싶지 않을 때 사용한다.
  • 보통 내부에서만 호출되어야 하는 메서드를 비공개 메서드로 만든다.

References