[파이썬 핵심 기초 다지기] 클래스

julian·2025년 2월 18일

python

목록 보기
9/74
post-thumbnail

📌 사용 환경

Python 3.10.2
conda 24.9.0
JupyterLab 4.2.5


1. 클래스

클래스(class)
인스턴스(instance)
객체(object)
속성(attribute) : 변수
메서드(method) : 함수
생성자(constructor)
소멸자(destructor)
self
상속, 함수 재정의(override), interface 등등

파이썬은 클래스 중심 언어가 아니기 때문에 개념만 좀 알아두고 간단하게 들어갈 생각이다.

객체 지향 프로그래밍의 핵심 개념 중 하나로, 데이터와 이를 처리하는 메서드를 함께 묶어 놓은 것이다.
이를 통해 프로그램을 보다 모듈화 하고 재사용 가능한 코드를 작성할 수 있다.
파이썬에 있는 이유는? 파이썬은 범용 S/W여서 이것 저것을 다 만들 수 있게 해놓은 것이다.

함수를 떠올려보면,

x=10
y=20
z=x+y

이렇게 썼다가 이걸 모아서 해주면 안될까? 해서 함수를 만들었었다.

def sub(a,b):
    c=a+b
    return c

그런데 이렇게 하니까 불편해서 클래스가 나온 것이다.

def 총점():
    ...

def 평균():
    ...

총점값=총점(10,20)
평균(총점값) #이런걸 새로 만들었음 그래서 평균 함수도 필요해짐

그런데 이걸 보면 전체가 평균을 구하는 것이기 때문에 하나로 묶는것이다. 이것이 클래스.
클래스명은 반드시 대문자로 시작한다. 필수!(네이밍규칙)

class ReportCard:
    def __init__(self, score1, score2):  # 생성자
        self.score1=score1
        self.score2=score2

    def total(self):
        return self.score1+self.score2

    def average(self):
        return (self.score1+self.score2)/2

    def show(self):
        print(f"총점: {self.total()}, 평균: {self.average()}")

# student 객체 생성
student1=ReportCard(75, 85)
student1.show()

student2=ReportCard(90, 95)
student2.show()

이렇게 하는 게 대략적인 클래스의 동작과정이다.
실제로는 다음과 같다.

class Counter:
    def __init__(self):  # 생성자(정해진 예약어)
        self.count=0  # 속성 정의

    def increase(self, amount):  # 메서드
        self.count+=amount

    def display(self):  # this
        print(self.count)

counter1=Counter()  # 객체, 인스턴스(메모리와 관련됨 -> 클래스를 만들면 메모리 공간에 올라감) 생성
counter1.increase(5)
counter1.display()

counter2=Counter()
counter2.increase(3)
counter2.display()

# ✅ 출력 결과
# 5
# 3

여기서 클래스 생성부를 보면 _, __ 과 같은 부분을 볼 수 있는데, 이런 것들이 특별한 역할을 한다.
먼저 __는 자동으로 실행하는 것이다. 즉 이 클래스를 호출하면 자동으로 실행되는 부분이다.
그렇다면? __init__ 객체가 생성될때 자동으로 실행된다는 것을 알 수 있다.

이제 _에 대한 내용과 좀 더 응용하는 부분은 아래서 확인하자.

1.1. 속성 접근 방법

class Car:
    def __init__(self):
        self.price=0  # 속성
        self._speed=0  
        self.__color=None

car=Car()
print(car.price)  
print(car._speed)  
#print(car.__color)
0
0

호출부를 보면 _가 붙지 않은 모습, _ 1개, __ 과 같이 2개 이런 다양한 모습을 볼 수 있다.
먼저 이야기 하자면, Java로 보았을때, 이는 접근 제한자를 생각하면 된다.

다시 클래스 선언부로 돌아가 설명하면 __init__ 함수의 속성들 중,
그리고 price는 public, _speed는 protected, __color는 private라고 생각하면 된다.

그래서 public인 price는 클래스 밖에서 접근이 가능했고, 사실 protected인 _speed도 원래는 상속을 받아 사용해야 하지만 파이썬에서는 사용은 가능하지만 가급적 사용하지 않는 편이다.
그리고 마지막 private인 __color는 class의 밖에서 사용하면 Error가 발생하기 때문에 주석으로 처리한 모습이다.

위 Calculator에서는 직접적으로 데이터에 접근하지 않았기에 괜찮았지만, 이는 car.price, car._speed와 같이 멤버 속성에 직접 접근했다.

사실 클래스의 특징은 데이터를 보호하기 위한 은닉화이다. 그래서 이 Car의 예시는 이렇게 쓰면 좋지 않다는 것을 보여준다.

따라서 원래라면 보통 __color와 같이 속성들을 다 private로 처리해주는 경우가 많다.

1.2. Property

그렇다면 __를 이용하면 클래스 밖에서 접근을 못하는데, 이를 접근 가능하게 할 수 있는 방법을 만들어놓았다.

  1. getter, setter
class Laptop:
    def __init__(self):
        self.__price=0
        self.__model="Unknown"

    def set_price(self, price):
        self.__price=price

    def get_price(self):
        return self.__price

    def set_model(self, model):
        self.__model=model

    def get_model(self):
        return self.__model

이렇게 속성들에 __가 있으면 클래스 외부에서 접근을 할 수 없으니, set과 get메서드가 있어야 접근가능하다.

따라서 이를 이용하여,

my_laptop=Laptop()
my_laptop.set_price(1500)
print(my_laptop.get_price())

# ✅ 출력 결과
# 1500

즉, 생성자로 만든 객체인 my_laptop이 self로 들어가게 된다고 생각하면 된다.
그러니 my_laptop.price=0 이런 식으로 바뀌게 되는 것이다.

그 이후 다시 your_laptop라는 새로운 객체로 만든다면 다시 my_laptop로 바뀐 부분이 싹 다 지워지고, your_laptop로 교체되어 다시 접근하게 되는 것이다.

그런데 가끔 your_laptop이라는 새로운 객체를 만들었을때, 값을 할당해주지 않았다면 이전 my_laptop의 값들을 새롭게 가진다고 생각할 수 있는데 그렇지 않다.
my_laptop과 your_laptop은 각각 독립적인 객체로, my_laptop에서 값을 설정한 후, your_laptop을 새로 생성하면 your_laptop은 새로운 인스턴스이므로 my_laptop에서 설정한 값은 your_laptop에 영향을 주지 않는다. 각 객체는 서로 다른 메모리 공간에 저장되며, 각각의 객체는 자신만의 속성 값을 가지고 있는다.

따라서, your_laptop을 만들고 값을 설정하지 않으면 your_laptop의 속성은 기본값 그대로 있는다.

  1. @property - @(Decorator)
class Book:
    def __init__(self):
        self.__title="Untitled"

    @property
    def title(self):
        return self.__title

    @title.setter
    def title(self, new_title):
        self.__title=new_title
b=Book()
b.title="Python Guide"
print(b.title)

# ✅ 출력 결과
# Python Guide
  1. property 함수 직접 선언
class Smartphone:
    def __init__(self):
        self.__storage=64

    def get_storage(self):
        return self.__storage

    def set_storage(self, size):
        self.__storage=size

    storage=property(get_storage, set_storage)
phone=Smartphone()
phone.storage=256
print(phone.storage)

# ✅ 출력 결과
# 256

1.3. 상속과 오버라이딩

Animal이라는 클래스를 상속받는 Dog와 Cat이라는 클래스를 구현해보자.

class Animal:
    def __init__(self):
        pass
    def move(self):
        print("움직였습니다.")
    def sound(self):
        print("동물의 소리")

class Dog(Animal):
    def sleep(self):
        print("쿨쿨")
    def sound(self):
        print("멍멍")

class Cat(Animal):
    def play(self):
        print("놀자~")
    def sound(self):
        print("야옹")
dog=Dog()
dog.move()
dog.sleep()
dog.sound()

# ✅ 출력 결과
# 움직였습니다.
# 쿨쿨
# 멍멍

이렇게 Animal이라는 클래스를 상속받을때는 Dog라는 클래스를 정의할때 class Dog(Animal)이라고 하여 ()안에 Animal이라는 클래스명을 넣어준다.

이렇게 새롭게 만든 dog라는 객체는 상위 클래스의 method까지 접근이 가능하고 dog.move()가 가능하다.
따라서, Dog 클래스에는 move()라는 method가 없지만 dog객체는 상위 클래스의 move()로 접근이 가능하여 "움직였습니다."를 출력하는 것이다.

또, sound()라는 method는 상위 클래스인 Animal에서 sound()는 "동물의 소리"라는 메세지를 출력하지만,
이를 상속받은 Dog클래스에서 이 sound()라는 method를 새롭게 재정의하여 사용할 수 있다. 이를 오버라이딩(Overriding)이라고 한다.
따라서 dog.sound()를 출력해보면 "멍멍"이라는 메세지를 출력하게 된다.

물론 dog가 Cat클래스의 play 메서드에는 접근할 수 없다!

dog.play()


💪 객관식 퀴즈

Q1. 클래스와 객체에 대한 설명으로 옳지 않은 것은?
1) 클래스는 설계도와 같다.
2) 객체는 클래스에서 정의된 속성과 메서드를 가지는 인스턴스이다.
3) 객체는 메모리에 한 번만 할당된다.
4) 여러 객체가 같은 클래스로부터 생성될 수 있다.

A1. 3 / 여러 객체를 생성할 수 있다.

Q2. __init__ 메서드에 대한 설명으로 옳은 것은?

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

1) 객체가 메모리에 할당될 때 자동으로 호출된다.
2) 객체의 속성을 초기화한다.
3) 객체를 출력하는 메서드이다.
4) 객체를 삭제하는 메서드이다.

A2. 2

Q3. 다음 클래스에 대한 설명으로 옳은 것은?

class Animal:
    def sound(self):
        print("동물의 소리")

class Dog(Animal):
    def sound(self):
        print("멍멍")

class Cat(Animal):
	def sound(self):
    	print("야옹")

class Wolf(Animal):
	pass

my_dog=Dog()
my_dog.sound()
cat=Cat()
cat.sound()
wolf=Wolf()
wolf.sound()

1) my_dog이라는 객체는 생성될 수 없다.
2) cat 클래스에서 sound 메서드를 추가하지 않았기 때문에 오버라이딩이 발생한 것이다.
3) Dog 클래스에서 sound 메서드를 재정의했기 때문에 오버라이딩이 발생했다.
4) wolf.sound()를 호출하면 에러가 발생한다.

A3. 3

Q4. 다음 다중 상속에 관한 설명 중 옳지 않은 것은?

class A:
    def method_a(self):
        print("Method A")

class B:
    def method_b(self):
        print("Method B")

class C(A, B):
    def method_c(self):
        print("Method C")

1) A와 B클래스의 기능을 C클래스에서 사용할 수 있다.
2) C클래스에서 method_amethod_b를 호출할 수 있다.
3) C클래스에서 method_a를 재정의할 수 있다.
4) 다중 상속은 C클래스에서 method_a만 사용할 수 있도록 제한한다.

A4. 4

Q7. 다음 중 옳은 것은?

class MyClass:
    def __init__(self, value):
        self.value = value

a = MyClass(10)
b = MyClass(10)

1) a == b는 True이다.
2) a == b는 False이다.
3) a == b는 에러가 발생한다.
4) a == b는 None을 반환한다.

A5. 2 / 메모리 주소가 다르므로

profile
AI Model Developer

0개의 댓글