Class & Instance & Object 직관적인 이해
C를 배워봐서 알겠지만 C에서는 클래스가 없다. 다시말하면 이는 클래스를 개념을 도입하지 않아도 충분히 프로그램을 만들 수 있다는 얘기와 같다. 그렇다면 왜 굳이 이 개념을 차용했을까?
귀찮음을 통해 인류문명이 발전했듯이 이 추상적인 개념을 도입한다면 우리가 얻는 이익은 굉장히 많다. 예제를 통해 생각해보자.
클래스는 어찌보면 인류역사에서 중요한 사건인 산업혁명에 비유될 수 있을 듯 하다. 산업혁명은 수공업으로 부터 공업으로 확산된 생산으로부터의 혁명이다. 기존의 제품을 만드는 것은 각각의 상점에서 수공업으로 하나하나의 상품을 만들어왔다. 그런데 만약 이 하나의 상품을 만드는데에 있어서 정해진 방법, 정해진 틀, 클래스가 있다면 어떨까?
예를 들어 붕어빵을 만든다고 하자.
만약 붕어빵을 만드는데에 있어 틀이 없이 만든다면, 우리가 설날에 전을 부치듯 하나하나를 모양을 잡아가며 만드는 것이 보통일 것이다. 이럴 경우 시간도 오래걸리고 번거롭다.
반대로 틀을 사용한다면 시간과 노력이 둘다 절약되어 만드는 것이 쉬워질 것이다.
여기서 틀 = Class, 틀에 의해 만들어진 붕어빵 = Instance(or Object) 이다.
즉 클래스는 똑같은 무엇인가를 계속해서 만들어낼 수 있는 설계 도면 같은 것이고, 객체란 클래스에 의해서 만들어진 피조물과 같은 개념이다. 결국 우리는 이 개념을 차용해서 사용할 경우 대량생산과 유지보수에 있어 이점을 얻을 수 있다.
출처 : 게티이미지뱅크
Class & Instance & Object 의 정확한 차이
사실 객체와 인스턴스 자체를 정확하게 구분하는 것은 힘들지만 나는 이렇게 이해하려고 한다. 사실 플라톤의 이데아 개념과 일맥상통하지 않나 생각하고 있다. 내가 만약 신(?)이고 세계를 만들어야된다고 가정해본다면,
Class = 설계도
Object = 설계도의 대상, 이데아
Instance = 설계도의 실체(Instance), The instance of Class
사실 객체를 클래스의 인스턴스라고도 부르기 때문에 정확한 비유는 아닐 수 있다. 여기서는 reference에 할당되었다는 의미를 강조하기 위해 저렇게 서술했다. (reference는 c에서 pointer와 같은 개념이라고 생각하면 된다.)
참고
클래스는 어떤 대상을 만들때 있어 설계도라고 말은 했지만 사실 다양한 대상들에 있어 같은 속성을 가지고 있을때, 이를 묶어서도 사용할 수 있다. 여기서는 기본적인 이해를 위해 저렇게 서술하였다.
Method
그런데 붕어빵을 만든뒤 우리가 하는 행위를 생각해보면,
- 식힌다.
- 먹는다.
- 손을 닦는다.
와 같이 어떤 동작을 한다.
이것들은 붕어빵과 관련된 동작들이고 파이썬 안에서는 함수 와 대응될 수 있을 것이다. Class안에 선언된 함수를 우리는 특별히 Method 라 부른다.
클래스 멤버 (The member of the class)
이제 코드안에서 클래스를 만들기 위해 어떤 녀석들이 필요한지 알아보자. 클래스 안에는 메서드(method), 속성(property), 클래스 변수(class variable), 인스턴스 변수(instance variable), 초기자(initializer), 소멸자(destructor)와 같이 여러것들이 들어갈 수 있다. 크게 나누면 데이터를 표현하는 필드와 행위를 표현하는 메서드로 구분할 수 있다. 둘은 그 객체의 attribute라 불린다. 객체가 가진 특징을 얘기한다고 생각하면 당연하다.
Field
- 클래스 변수
클래스를 정의할 때 메서드 밖에 존재하는 변수를 클래스 변수라고 한다. 클래스 전체에 적용되는 변수이다.
Rectangle.count
로 접근할 수 있다.
- 인스턴스 변수
하나의 클래스로 부터 여러 객체 인스턴스를 생성해서 사용할 수 있다. 클래스 내부에서 접근할때는 "self."를 사용해야 한다. 클래스 외부에서는
Rectangle.width
와 같이 "객체변수.인스턴스변수"와 같이 접근한다.
Example
class Rectangle:
count = 0 # 클래스 변수
# 초기자(initializer)
def __init__(self, width, height):
# self.* : 인스턴스변수
self.width = width
self.height = height
Rectangle.count += 1 # 여기 안에서도 호출할 때 클래스변수는 이렇게 호출
# 메서드
def calcArea(self):
area = self.width * self.height # self의 사용예, 사용하지 않으면 오류가 뜬다.
return area
보통 instance 변수를 많이 쓰게될 것을 직감적으로 알 수 있다.
Method
- initializer
클래스로부터 새 객체를 생성할 때마다 실행되는 특별한 메서드이다. 위의 예에서 __init__
이라고 선언된 메서드가 있는데, 이를 말하는 것이다. (주: 파이썬에서 두개의 밑줄 (__) 시작하고 두개의 밑줄로 끝나는 레이블은 보통 특별한 의미를 갖는다.) Initializer는 클래스로 부터 객체를 만들 때, 인스턴스 변수를 초기화하거나, 객체의 초기상태를 만들기 위한 문장들을 실행하는 곳이다.
- Instance Method
위의 예에서 __init__
과 같이 인스턴트 변수를 사용하는 메서드를 의미한다.
- Static Method
정적 메서드는 self.
를 가지고 접근이 불가하다. 따라서 보통 객체와 독립적이지만 포함될 때 사용된다. @staticmethod
와 같은 표시자를 사용한다.
- Class Method
클래스 메서드는 정적 메서드와 비슷한데, 객체 자신의 인스턴스 변수를 의미하는 self.
를 사용하지 않고 cls
라는 클래스를 의미하는 파라미터를 전달 받는다.
Example
class Rectangle:
count = 0 # 클래스 변수
def __init__(self, width, height):
self.width = width
self.height = height
Rectangle.count += 1
# 인스턴스 메서드
def calcArea(self):
area = self.width * self.height
return area
# 정적 메서드
@staticmethod
def isSquare(rectWidth, rectHeight):
return rectWidth == rectHeight
# 클래스 메서드
@classmethod
def printCount(cls):
print(cls.count)
# 테스트
square = Rectangle.isSquare(5, 5)
print(square) # True
rect1 = Rectangle(5, 5)
rect2 = Rectangle(2, 5)
rect1.printCount() # 2
객체의 생성과 사용
# 객체 생성
r = Rectangle(2, 3)
# 메서드 호출
area = r.calcArea()
print("area = ", area)
# 인스턴스 변수 엑세스
r.width = 10
print("width = ", r.width)
# 클래스 변수 엑세스
print(Rectangle.count) # Correct
print(r.count) # Wrong
클래스 변수는 꼭 클래스명.클래스변수명
으로 써주어야 한다. 잘못된 예시결과를 보자.
r = Rectangle(2, 3)
Rectangle.count = 50
r.count = 10 # count 인스턴스 변수가 새로 생성됨
print(r.count, Rectangle.count) # 10 50 출력
이렇게 새로운 변수가 생성되어 원하는 결과와 다른 결과를 출력하게 된다.
클래스 상속(class inherence) & 다형성(polymorphism)
class Animal:
def __init__(self, name):
self.name = name
def move(self):
print("move")
def speak(self):
pass
class Dog (Animal):
def speak(self):
print("bark")
class Duck (Animal):
def speak(self):
print("quack")
위와 같이 사용가능하다.
실제 선언방법은 다음과 같다.
dog = Dog("doggy") # 부모클래스의 생성자
n = dog.name # 부모클래스의 인스턴스변수
dog.move() # 부모클래스의 메서드
dog.speak() # 파생클래스의 멤버
또한 다음과 같이 다형성도 지원한다.
animals = [Dog('doggy'), Duck('duck')]
for a in animals:
a.speak()
같은 메서드가 객체 안에 들어있으므로 서로다른 speak() 메서드가 호출된다.