클래스 변수와 인스턴스 변수
1. 클래스 변수
클래스 변수는 클래스 바로 하위에 자리하고 있는 변수들입니다. 이 클래스 변수는 아래의 예시와 같이 클래스 이름, 인스턴스 이름을 통해서 접근할 수 있습니다.
이 클래스 변수는 해당 클래스를 통해 만들어진 모든 인스턴스 객체들이 공유하는 변수 값입니다. 반면 인스턴스 변수는 각 인스턴스 객체가 고유하게 가지고 있는 변수 값입니다. 이 클래스 변수는 클래스 이름을 통해 접근할 수 있습니다. 또한 인스턴스 객체를 통해서도 접근할 수 있습니다.
위와 같이 작성하면 modely의 max_speed는 500이 됩니다. 모든 변수가 공유하는 변수라고 하였으니 modelx의 값도 바뀌어야 할 것 같지만 modely.max_speed 값을 조정하는 것은 modely의 인스턴스 변수의 값을 조정하는 것과 같습니다.
아래와 같이 바꾸면 modelx의 값도 바뀌게 됩니다.
모든 값이 수정된 것을 확인할 수 있습니다. 이는 실제로 두 개의 인스턴스가 max_speed
, max_people
각각의 메모리 영역이 아니라 하나의 메모리 영역을 공유하고 있다는 것을 말해줍니다.
다음 예제는 조금 어렵습니다. 헷갈리지 않도록 주의해주세요. 아래 코드를 주석을 풀고, Car의 speed 값을 변경하는 코드는 주석 처리하겠습니다.
출력 값이 어떻게 되었을지 예상해보세요. 분명 클래스 변수를 수정한 것 같은데 두 수의 스피드가 공유되고 있지 않습니다. 뒤에 수정된 변수가 반영되어서 둘 다 250을 출력하는 것이 합당해 보입니다.
def change_speed(self, speed):
self.speed = speed
def change_speed(self, speed):
self.speed = speed
여기서 self.speed는 상위에 있는 speed를 조작하지 않습니다. 이는 인스턴스 변수가 생성된 것 뿐입니다. 따라서 아래처럼 출력이 됩니다.
# 출력
modelx.speed: 500
modely.speed: 250
# 출력
modelx.speed: 500
modely.speed: 250
그렇다면 300은 어떻게 된 것인지 확인해보도록 하겠습니다. 아래 코드를 실행해보세요.
300은 그대로 클래스 변수에 남겨져 있습니다. 이는 마치 아래 코드와 같습니다.
이렇듯 헷갈리는 예제를 준비한 것은 각각의 메모리 영역을 명확하게 이해할 수 있어야 견고한 코드를 짤 수 있기 때문입니다. 클래스의 메모리 영역이 어디인지, 인스턴스의 메모리 영역이 어디인지 구분할 필요가 있습니다.
다음 예제는 위 예제와 비슷하게 보이지만 실제로는 클래스 변수를 수정하는 예제입니다.
앞 클래스에서kinds
배열의 경우에는 add_kinds 메서드 안에서 각 인스턴스 객체가 해당 배열의 주소를 참조하고 있어, 그 배열의 주소에 접근하여 조작했기 때문에 클래스 변수를 직접 수정한 것처럼 동작을 하게 된 것입니다. 반면에 speed
의 경우에는 change_speed 메서드 안에서 인스턴스 변수를 생성하여 값을 가지게 된 것입니다. 이는 함수에서 리스트가 전달되고 함수 내부에서 리스트를 조작하면 원본 리스트도 변경되는 것과 같은 원리입니다.
다만 이렇게 수정하게 하였을 경우에 인스턴스 변수를 수정하는 것인지, 클래스 변수를 선언하는 것인지 헷갈릴 수 있어 아래와 같이 수정하는 것이 좋습니다. 추후에 배울 @classmethod
데코레이터를 사용하면 클래스 변수를 수정하는 것임을 명확하게 할 수 있습니다.
2. 인스턴스 변수
인스턴스 변수는 클래스 변수와 다르게 각 인스턴스 객체가 가지고 있는 변수입니다. 클래스 변수는 클래스가 메모리에 로드될 때 생성되지만, 인스턴스 변수는 인스턴스 객체가 생성될 때 생성됩니다.
인스턴스 변수는 self가 위치한 어디서나 선언이 가능하지만 보통 __init__
메서드 안에서 선언됩니다. __init__
메서드는 다른 프로그래밍 언어에서 생성자(constructor
)라 불립니다. 언더바가 2개 앞뒤로 있는 메서드는 매직 메서드, 던더 함수라고 불립니다. 우리는 있는 그대로 이닛(__init__
) 매직 메서드라고 말하도록 하겠습니다. 이 메서드는 인스턴스 객체를 생성할 때 자동으로 실행됩니다.
self가 있는 곳에서 선언된 변수들은 인스턴스의 영역으로 간주되어 dot으로 호출할 수 있습니다. 이 변수는 다른 인스턴스와 메모리 영역을 공유하지 않습니다. 또한 이 값은 아래와 같이 추가 또는 변경이 될 수 있습니다.
3. 다른 자료형의 인스턴스 변수 변경
우리는 hello의 type을 출력해 봄으로써 함수도 class라는 것을 알 수 있습니다. 그렇다면 함수에 인스턴스 변수를 생성할 수 있는지 확인해보도록 하겠습니다.
인스턴스 변수를 넣어보도록 하겠습니다.
hello.hi = 'hi, world'
hello.hi # 출력: hi, world
hello.hi = 'hi, world'
hello.hi # 출력: hi, world
다만 기본으로 내장되어 있는 자료형은 대부분 이를 허락하고 있지 않습니다.
4. 클래스, 인스턴스 변수 요약
중요한 내용이니 요약하고 가도록 하겠습니다.
4.1 클래스 변수
- 정의: 클래스 변수는 클래스 정의 내에 선언되며, 클래스의 모든 인스턴스 간에 공유됩니다. 클래스 변수는 클래스가 메모리에 로드될 때 생성됩니다.
- 용도: 클래스 변수는 해당 클래스의 모든 인스턴스에서 공통적으로 사용되는 값을 저장하는 데 사용됩니다. 예를 들어, 모든
Car
객체에 공통적으로 적용되는 기본 속도 설정과 같은 경우에 사용됩니다. - 접근 방법: 클래스 이름을 통해 접근하거나
self
를 통해 인스턴스를 통해서도 접근할 수 있습니다.Car.max_speed
와 같은 형태로 사용할 수 있습니다. - 특징: 클래스 변수의 값을 변경하면, 해당 클래스의 모든 인스턴스에 대해 변경사항이 적용됩니다.
4.2 인스턴스 변수
- 정의: 인스턴스 변수는 클래스의 각 인스턴스(객체)에 속하는 변수입니다. 이 변수들은 각 인스턴스마다 별도로 존재하며, 인스턴스 생성 시에 만들어집니다.
- 용도: 인스턴스 변수는 개별 객체의 상태를 저장하는 데 사용됩니다. 예를 들어, 여러
Car
객체가 있을 때 각Car
의 속도나 색상 같은 고유한 속성을 인스턴스 변수로 저장할 수 있습니다. - 접근 방법:
self
키워드를 사용하여 인스턴스 내에서 이 변수에 접근하고 수정합니다.self.max_speed = 200
와 같은 형태로 사용할 수 있습니다. - 특징: 각 인스턴스의 변수는 서로 독립적이므로, 한 인스턴스에서 변수의 값을 변경해도 다른 인스턴스의 동일한 변수에는 영향을 주지 않습니다.