클래스

애늙은이·2023년 7월 30일
0
post-thumbnail

클래스라는 개념은 파이썬에서 가장 중요한 개념이라고 할 수 있습니다. 파이썬이라는 언어가 추구하고자 하는 바를 담았기 때문이죠. 그렇기에 클래스를 살펴보기 전, 먼저 파이썬이 추구하는 객체지향 프로그래밍에 대해 알아볼 필요가 있습니다.

💡 객체지향 프로그래밍에 관하여

객체 지향 프로그래밍(Object Oriented Programming)은 프로그래밍의 패러다임 중 하나입니다. 말 그대로 프로그래밍을 설계할 때, 객체들의 상호작용을 중심으로 두자는 이야기죠. 하지만, 객체라고 하면 무언가 명확하게 떠오르지 않고 추상적으로만 느껴집니다. 따라서 객체가 무엇인지 알아야 할 필요가 있습니다.

🤔 객체란?

객체(Object)는 우리가 사는 세계의 사물, 존재 등에서 착안한 개념입니다. 쉽게 사람이라고 예시를 들어보죠.
사람은 크게 두 가지로 나눌 수 있습니다. 사람이 가진 특징과 행동으로 말이죠. 사람의 특징으로는 나이나 이름 등을 들 수 있고, 사람의 행동으로는 걷기, 뛰기 등이 있겠네요. 즉, 다음과 같이 정리할 수 있습니다.

객체 또한 특징과 행동(기능)으로 이루어져 있습니다. 객체에선 이를 각각 속성과 메소드로 부릅니다.

개념정의
속성(attribute)객체에 포함된 변수나 데이터
메소드(method)객체에 포함된 함수

객체는 단순히 홀로 존재하는 것이 아닙니다. 사람이 다른 사람과 관계를 맺듯, 객체 또한 다른 객체와 여러 관계를 맺는 경우가 많습니다. 객체지향 프로그래밍에서는 이러한 객체들의 관계를 중점적으로 보여주면서 보다 직관적인 구조를 제공합니다.

🤔 클래스란?

클래스는 객체를 찍어내는 틀입니다. 즉, 객체를 만들기 위해 필요한 설계도와 같은 역할을 하죠. 따라서 클래스는 객체의 속성과 메소드를 정의하는데 사용됩니다.
예를 들어 사람을 클래스로 만든다고 한다면, Jack, Bruce, Tyler와 같은 객체들이 있을 수 있습니다. 이들은 개별적으로는 차이가 있을 수 있으나 모두 사람이라는 클래스의 속성과 메소드를 가지고 있죠.

이렇듯, 클래스를 통해서 만들어낸 객체는 각각의 속성과 공통된 메소드를 가지고 있습니다. 때문에 클래스를 사용하면 코드를 간결하게 쓸 수 있습니다.

🔨 클래스의 구조

✔ 일반적인 클래스의 구조

클래스는 일반적으로 다음과 같은 구조로 정의됩니다.

class 클래스명:
    def __init__(self, 매개변수):
        # 초기화 내용
    def 메소드_이름(self, 매개변수):
        # 메소드 내용

클래스는 class 키워드를 사용하여 정의하고, 클래스명을 적어줍니다. 여기서 한 가지, __init__이라는 메소드가 눈에 띱니다.
__init__ 메소드는 클래스의 객체가 생성될 때 호출되는 특별한 메소드로, 주로 생성되는 객체의 속성을 적습니다. 클래스 내에 정의된 다른 메소드들은 해당 클래스의 객체를 통해 호출할 수 있습니다.

개념표현방식
속성self.속성명
메소드def 메소드명(self, 매개변수):

코드를 통해 자세히 살펴보겠습니다.

# 클래스 이름 첫 글자는 주로 대문자로 표기합니다.
class Human: 
  def __init__(self, name, age):
    self.name = name
    self.age = age
  
  def walk(self):
    print("walk!", self.name)

jack = Human("Jack", 21)
bruce = Human("Bruce", 40)
tyler = Human("Tyler", 36)

print(jack.age)
jack.walk()

# 결과
# 21
# walk! Jack

위의 코드를 살펴보면, __init__ 메소드에 name과 age라는 속성을 만든 것을 보실 수 있습니다. 때문에 jack이라는 객체를 만들 때도 "Jack"이라는 name과 21이라는 age를 전달해주는 것을 확인할 수 있습니다.

또한 만든 객체 뒤에 .을 붙여 속성을 가져오거나 메소드를 사용할 수 있습니다. 위의 코드에서도 jack 뒤에 .을 붙이고, age나 walk()를 적어 jack이 가지고 있는 속성과 메소드를 사용하는 걸 볼 수 있죠.

✔ self

아까의 코드를 보면, __init__ 메소드에는 인자가 self, name, age로 세 개가 존재했습니다. 그렇지만 막상 쓸 때는 name과 age만 전달했죠. self는 어디로 간 걸까요?

self는 클래스로 만들어진 객체를 전달하는 역할을 합니다. 즉, self로 객체 자기 자신을 전달해 주면서, 매개변수로 온 name과 age가 객체의 속성으로 자리잡게 해주죠. 다른 메소드에서도 self를 통해 해당 객체의 속성이나 메소드를 사용할 수 있게 합니다.

class Student:
  def __init__(self, name, score):
    self.name = name
    self.grade = score # 매개변수로 온 score를 grade라는 속성의 값으로 넣고 있습니다.
  
  def check(self):
    return self.grade # 객체의 성적을 돌려줍니다.
    
  def report(self):
    grade = self.check() # self를 통해서 check() 메소드를 실행합니다.
    print(self.name, grade)
    
jack = Student("Jack", 90)
jack.report()

# 결과
# Jack 90

위의 코드를 통해 self를 사용하여 jack이라는 객체의 속성과 메소드를 쓸 수 있다는 걸 알 수 있습니다.

🎈 인스턴스

클래스의 객체를 인스턴스라고도 부릅니다. 인스턴스는 객체와 혼동되어 사용하지만 클래스와의 관계에 주목한 개념입니다. jack = Human()일 때, jack은 객체이자 Human 클래스의 인스턴스인 셈이죠. 때문에 인스턴스 역시 속성을 가지고 있으며, 메소드를 호출하여 특정한 작업을 수행할 수 있습니다.

🔗 상속

✔ 상속이란?

앞에서 객체는 홀로 존재하는 것이 아닌 여러 객체들과 관계를 맺는다고 했습니다. 이는 객체의 틀인 클래스도 마찬가지입니다. 클래스 또한 여러 클래스와 계층 관계를 맺죠. 이러한 관계를 드러내는 것이 바로 상속입니다.

상속은 말 그대로 부모가 자식에게 물려주는 것입니다. 클래스도 부모-자식 관계로 부모 클래스가 자식 클래스에게 속성과 메소드를 물려줍니다.

용어개념
부모 클래스(Parent/Super Class)속성과 메소드를 물려주는 클래스
자식 클래스(Child/Sub Class)속성과 메소드를 물려받는 클래스

✔ 왜 상속을 사용하는가?

상속은 클래스 간의 관계를 보여주기도 하지만, 코드를 재사용하고 확장할 수 있다는 점에서 이점을 가집니다.
만약 사람이라는 클래스가 있다고 해봅시다.

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

여기서 이름, 나이, 성적을 속성으로 가진 학생 클래스를 만든다면 어떻게 해야 할까요? 학생 클래스를 새로 만드는 방법도 있겠지만, 사람 클래스를 상속받아 성적 속성만 추가하는 방법이 코드 활용성 측면에서 더 좋습니다.

✔ 클래스 상속

파이썬에서 클래스 상속은 class 자식_클래스명(부모_클래스명)로 이루어집니다.

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age
    
class Student(Person):
    def __init__(self, name, age, grade):
       super().__init__(name, age)
       self.grade = grade
       
jack = Student("Jack", 21, 90)

자식 클래스인 Student 클래스의 __init__ 메소드를 보면, super().__init__()이라는 보지 못했던 구문이 있는 것을 확인할 수 있습니다. super().__init__()는 부모 클래스인 Person 클래스의 __init__ 메소드를 가져오는 것입니다. 때문에 Person 클래스의 속성인 name과 age를 인자로 받는 것을 볼 수 있죠.

✔ 메소드 오버라이딩

우리는 부모 클래스로부터 속성뿐만 아닌, 메소드도 상속받을 수 있다고 배웠습니다. 하지만, 만약 부모 클래스에서 상속받은 메소드를 변경하고 싶다면 어떻게 해야 할까요? 파이썬에서는 메소드 오버라이딩을 통해 이를 구현합니다.

메소드 오버라이딩은 자식 클래스에서 부모 클래스의 메소드를 재정의함으로써 이루어집니다.

class Person: # 부모 클래스
  def __init__(self, name):
    self.name = name
 
  def hello(self):
    print("hello! I'm", self.name)

class Student(Person): # 자식 클래스
  def __init__(self, name):
    super().__init__(name)
    
  def hello(self):
    print("hello! I'm student", self.name)
    
a = Person("Kim")
b = Student("Park")

a.hello()
b.hello()

# 결과
# hello! I'm Kim
# hello! I'm student Park

위의 코드를 보면, Person 클래스를 상속받은 Student 클래스에서 hello 메소드를 재정의한 것을 볼 수 있습니다. 즉, hello 메소드에 대해서 메소드 오버라이딩이 발생한거죠.
메소드 오버라이딩은 구조적인 측면에서 확장성을 증가시킨다는 점에서 의의를 가집니다. 즉, 부모 클래스에서 자식 클래스로 갈수록 기능이 확장되고 복잡해지는 구조를 가지게 한다는 것이죠.

♻ 다중 상속

다중 상속은 부모 클래스가 여러 개인 경우를 말합니다. 이때는 부모 클래스를 콤마(,)로 구분하여 적습니다.

class A:
  def hello(self):
    print("hello!")

class B:
  def welcome(self):
    print("welcome!")

class C(A, B):
  def hey(self):
    print("hey!")

c = C()
c.hello()
c.welcome()
c.hey()

# 결과
# hello!
# welcome!
# hey!

위의 코드를 보시면, A 클래스와 B 클래스를 다중 상속 받은 C 클래스가 두 클래스의 메소드 모두 사용할 수 있는 걸 확인할 수 있습니다.

📚 자료형과 클래스

우리는 인스턴스에 .을 찍어 관련 속성이나 메소드를 가져올 수 있었습니다. 점을 찍어 함수를 가져온다라.... 어디서 많이 보시지 않았나요? 바로 리스트에서 배웠던 리스트 관련 함수입니다. 사실 리스트는 클래스의 일환이었던 것이죠.

리스트뿐만이 아닙니다. 파이썬에서 우리가 사용하는 int, float, str, tuple 등의 자료형은 모두 클래스로 이루어져 있습니다. 실제로 int의 내부구조를 살펴보면 다음과 같습니다.

❗ 참고
본 시리즈에서는 파이썬을 깊게 다루지 않기에 @property@overload에 대해 잘 모르실 수도 있습니다. 그냥 int가 클래스로 이루어졌구나 정도로 이해하시면 되겠습니다.

때문에 .을 통해서 자료형과 관련된 속성과 메소드를 가져올 수도 있습니다.

a = 10

b = a.__add__(3) # __add__라는 덧셈 메소드를 사용했습니다.
print(b)

# 결과
# 13
lst = [1, 2, 3, 4, 5]

lst.append(6) # 리스트 클래스의 append라는 메소드를 사용했습니다.
print(lst)

# 결과
# [1, 2, 3, 4 ,5, 6]

또한 알아두면 좋을 것들에서 배운 자료형 변환도 사실 바꾸고자 하는 값을 새로운 자료형 클래스에 주는 방식입니다. 예를 들어, float(10)라고 한다면, float 클래스에 10을 줘 새로운 인스턴스를 만든 것이죠.

a = 17

float(a) # 새로운 float 클래스의 인스턴스를 만듦을 의미합니다.
profile
글쓰는 개발자입니다.

0개의 댓글