클래스 변수와 인스턴스 변수
1. 클래스 변수
클래스 변수는 클래스 바로 하위에 자리하고 있는 변수들입니다. 이 클래스 변수는 아래의 예시와 같이 클래스 이름, 인스턴스 이름을 통해서 접근할 수 있습니다.
이 클래스 변수는 해당 클래스를 통해 만들어진 모든 인스턴스 객체들이 공유하는 변수 값입니다. 반면 인스턴스 변수는 각 인스턴스 객체가 고유하게 가지고 있는 변수 값입니다. 이 클래스 변수는 클래스 이름을 통해 접근할 수 있습니다. 또한 인스턴스 객체를 통해서도 접근할 수 있습니다.
위와 같이 작성하면 modely의 max_speed는 500이 됩니다. 모든 변수가 공유하는 변수라고 하였으니 modelx의 값도 바뀌어야 할 것 같지만 modely.max_speed 값을 조정하는 것은 modely의 인스턴스 변수의 값을 조정하는 것과 같습니다.
아래와 같이 바꾸면 modelx의 값도 바뀌게 됩니다.
모든 값이 수정된 것을 확인할 수 있습니다. 이는 실제로 두 개의 인스턴스가 max_speed
, max_people
각각의 메모리 영역이 아니라 하나의 메모리 영역을 공유하고 있다는 것을 말해줍니다.
2. 인스턴스 변수
인스턴스 변수는 클래스 변수와 다르게 각 인스턴스 객체가 가지고 있는 변수입니다. 클래스 변수는 클래스가 메모리에 로드될 때 생성되지만, 인스턴스 변수는 인스턴스 객체가 생성될 때 생성하거나 생성되고 난 이후에 생성할 수 있습니다.
인스턴스 변수는 self가 위치한 어디서나 선언이 가능하지만 보통 __init__
메서드 안에서 선언됩니다. __init__
메서드는 다른 프로그래밍 언어에서 생성자(constructor
)라 불립니다. 언더바가 2개 앞뒤로 있는 메서드는 매직 메서드, 던더 함수라고 불립니다. 우리는 있는 그대로 이닛(__init__
) 매직 메서드라고 말하도록 하겠습니다. 이 메서드는 인스턴스 객체를 생성할 때 자동으로 실행됩니다.
여기서 self
라는 키워드에 주목할 필요가 있습니다. self
는 인스턴스 객체를 가리키는 키워드입니다. 이를 통해 인스턴스 변수를 생성하고 접근할 수 있습니다. self
가 있는 곳에서 선언된 변수들은 인스턴스의 영역으로 간주되어 dot으로 호출할 수 있습니다. 이 변수는 다른 인스턴스와 메모리 영역을 공유하지 않습니다. 또한 이 값은 아래와 같이 추가 또는 변경이 될 수 있습니다.
3. 인스턴스와 클래스 변수의 변경
이번 예제는 조금 어렵습니다. 헷갈리지 않도록 주의해주세요.
- 클래스 변수 speed는 300으로 초기화되어 있습니다.
- change_speed 메서드는 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은 그대로 클래스 변수에 남겨져 있습니다. 이는 마치 아래 코드와 같습니다.
이렇듯 헷갈리는 예제를 준비한 것은 각각의 메모리 영역을 명확하게 이해할 수 있어야 견고한 코드를 짤 수 있기 때문입니다. 클래스의 메모리 영역이 어디인지, 인스턴스의 메모리 영역이 어디인지 구분할 필요가 있습니다.
다음 예제는 위 예제와 비슷하게 보이지만 실제로는 클래스 변수를 수정하는 예제입니다.
add_kinds
에서 다루고 있는 self.kinds
는 인스턴스 변수가 아닙니다. 이는 지역변수가 전역변수를 읽을 수는 있듯이 클래스 변수를 읽어온 값입니다. 여기서 할당을 하게 되면 self.kinds
는 인스턴스 변수가 되지만 여기서는 self.kinds.append
로 참조만 하고 있기 때문에 이제부터는 클래스 변수에 값을 수정할 수 있게 됩니다.
반면에 speed
의 경우에는 change_speed 메서드 안에서 인스턴스 변수를 생성하여 값을 가지게 된 것입니다. 이는 함수에서 리스트가 전달되고 함수 내부에서 리스트를 조작하면 원본 리스트도 변경되는 것과 같은 원리입니다.
다만 이렇게 수정하게 하였을 경우에 인스턴스 변수를 수정하는 것인지, 클래스 변수를 선언하는 것인지 헷갈릴 수 있어 아래와 같이 수정하는 것이 좋습니다. 추후에 배울 @classmethod
데코레이터를 사용하면 클래스 변수를 수정하는 것임을 명확하게 할 수 있습니다.
4. 인스턴스 변수 변경
인스턴스가 생성되면 인스턴스에 dot을 통해 변수를 추가하거나 변경할 수 있습니다.
이러한 원리로 우리가 만든 함수에도 인스턴스 변수를 추가할 수 있습니다. 함수도 type을 출력해보면 class이기 때문입니다. 그렇다면 함수에 인스턴스 변수를 생성할 수 있는지 확인해보도록 하겠습니다.
인스턴스 변수를 넣어보도록 하겠습니다.
hello.hi = 'hi, world'
hello.hi # 출력: hi, world
hello.hi = 'hi, world'
hello.hi # 출력: hi, world
다만 기본으로 내장되어 있는 자료형은 대부분 이를 허락하고 있지 않습니다.