본문 바로가기

카테고리 없음

[Python 29] 객체의 상태와 인스턴스 변수

 

 

 

○ 객체의 행위는 객체의 속성(attributes) 값에 따라 달라진다

 

f( )라는 이름의 행위(behaiour) 를 가지고 있는 A 클래스에 대해 생각해 보자. 만약 a 가 A의 객체라면, a는 f( ) 라는 행위를 할 수 있다는 뜻이다. 하지만, 모든 A 객체들이 f( ) 행위를 동일하게 하지는 않는다. 행위의 방식은 동일하더라도 겉으로 드러난 행위는 서로 다르게 보일 수 있다. 예를 들어, 모든 Car 객체들은 move( ) 할 수 있다. 하지만, 어떤 Car 객체들은 3초 안에 시속 100 미터에 도달할 수 있을 만큼 "빠르게" move( ) 하는데 비해, 또 다른 Car 객체들은 1분 이상 달려도 시속 100미터에 도달하지 못할 만큼 상대적으로 "느리게" move( ) 한다. 이런 차이는 왜 생기는가? 사실 Car의 무게(weight) 와 배기량(cc)이 Car 객체가 "어떻게" move( ) 하는지를 결정하는데 영향을 줄 것이다. 무게가 무거울수록 느리게 move( )하는 경향이 강해질 것이고, 배기량이 클수록 빠르게 move( )하는 경향이 커질 것이다. 여기서, 자동차(Car)의 무게와 배기량 같이 각 자동차가 가지는 고유한 어떤 특성들이 그 자동차(Car) 객체의 행위에 영향을 미치게 되는데 이러한 특성들을 객체의 속성 (attributes) 이라고 부르며, 일반적으로 객체의 인스턴스 변수(instance variables)로 표현된다.

 

세상의 모든 객체들의 특성(characteristics)은 정적인 특성과 동적인 특성의 2가지 타입으로 구분할 수 있다. 예를 들어, 위의 Car 객체들의 경우 무게(weight), 배기량(cc), 색깔(color),  크기(size)  등의 정적인 특성을 가질 뿐 아니라, 자동차 객체들은 기본적으로 move( ) 할 줄 알아야 하고, 회전 (turn) 할 줄 알아야 하며, 정지한 상태에서 start( ) 할 수 있어야 하고, 주행하다가 목적지에 도달하면 stop( ) 할 수 있어야 한다. 이러한 특성들은 모두 Car 객체를 설명하기 위해 클래스의 정의에 포함되는 멤버(member)들이 되는데, 정적인 특성들은 데이터로 표현할 수 있는 부분이어서 보통 데이터 멤버(data members)라고 부르고, 행위적 특성은 함수로 표현되는 부분이어서 메쏘드 멤버(method members) 라고 부르기도 한다. 데이터 멤버를 인스턴스 변수 (instance variables)라고 부른다.

 

그런데, 이 두가지 특성이 너무나도 당연하게 서로 밀접하게 연관되어 있음을 알아야 한다. 위의 Car 예에서 보다시피 모든 Car 들이 move( ) 할 수 있지만 (즉, move( ) 못하는건 Car라고 부를 수 없다는 뜻) 각 Car가 가지는 무게와 배기량 속성에 따라 move( ) 하는 외양적 형태가 다르게 나타난다는 것이다. 즉,  move( )하는 성능이 달라진다는 뜻이고, 결국 모든 Car 들이 서로 다르게 move( ) 한다는 뜻이다. 

 

○ 인스턴스 변수 (instance variables)의 초기화

 

객체가 가지는 인스턴스 변수에 대해 조금 더 자세히 알아보도록 하자. 사람 객체들이 자신의 이름과 헤어 칼라를 생존하는 기간 내내 유지하는 것 처럼, 객체들도 인스턴스 변수들을 라이프 사이클 (Life Cycle) 내내 유지한다. 그리고, 그러한 변수들은 클래스의 모든 메쏘드 멤버들과 연관을 가지다 보니 각 메쏘드 멤버에서 이 변수들에 대한 접근이 가능해야 한다. 그러다 보니, 객체변수들은 각 메쏘드 함수 내에서만 사용되는 지역변수 (local variables) 가 되어서는 안되며, 특별히 인스턴스 변수임을 나타내는 장치가 필요하다.  파이썬에서는 클래스 내에서 self. 으로 시작하는 모든 변수들이 인스턴스 변수가 된다.

 

아래의 Car 클래스에서 사용되고 있는 speed 라는 변수는 self. 와 결합하여 사용되고 있기 때문에 인스턴스 변수이다.  move( ) 함수에서 self.speed 변수는, move( ) 함수 내에서 self 라는 이름으로 참조되는 (self라는 이름으로 가리켜 지는 바로 그) 객체의 speed 속성 값이라는 의미로 해석할 수 있다.

 

 

인스턴스 변수들에 대해 특별히 조심해야 하는 사항은 이들 변수의 초기화(initializataion, 최초의 assignment) 를 위해 특별한 장치가 사용되고 있다는 점이다. 어떤 임의의 함수에서 이들 변수를 초기화하는 것은 이 함수가 호출되지 않은 경우 소기의 목적을 달성하기 어렵고, 설령 그 함수가 호출되더라도 그 인스턴스 변수 값이 다른 함수에서 사용되기 전에 초기화된다는 확신을 가질 수 없다. 그래서 객체를 생성할 때 초기화할 수 있도록 특별한 장치를 제공한다.

 

모든 클래스들은 __init__( ) 이라는 특별한 이름의 함수를 가질 수 있으며 그 내용을 수정할 수 있다 (이를 오버라이딩 overriding 이라고 하며, 그 의미는 나중에 상속 inheritance 을 공부할 때 자세히 살펴보도록 하자) 이 함수가 만약에 클래스 내에 선언되어 있으면, 해당 클래스의 객체를 생성할 때 무조건 호출되며, 이 함수 내에서 인스턴스 변수들을 초기화할 수 있다. __init__( ) 함수의 이름은 init 이란 함수 이름 앞과 뒤에 언더스코어( _ ) 심볼이 2개씩 붙어 있는 형태이다. 

 

정리하자면, 만약 인스턴스 변수가 있고 초기화가 필요한 경우 (인스턴스 변수가 있으면 무조건 초기화가 필요하다라고 생각하자) 에는 __init__( ) 함수에서 초기화할 수 있으며, 이 함수는 객체를 생성할 때 자동으로 호출되게 된다. 위의 Car 클래스 예제를 다시 보자. 아래의 코드에서 보는 것 처럼 In[25]에서 새로운 Car 객체를 생성하고자 하는 순간에 __init__( )함수가 호출됨을 확인할 수 있다. 즉, 객체를 생성할 때 최초로, 그것도 자동적으로 호출되는 함수이니 인스턴스 변수들을 초기화할 수 있는 최고의 수단이 된다. 

 

 

예를 들어, 우리의 Car 객체들은 처음 생성될 때 speed 값이 0(제로) 으로 초기화된다고 한다면, 다음과 같이 __init__( ) 함수 내에서 self.speed를 초기화할 수 있다. __init__( ) 함수 내에서 self.speed = 0 으로 초기화되고 있음을 확인해 주기 바란다. Car의 move( )는 의미상으로 가속(acceleration) 이므로 함수의 이름도 accelerate( ) 로 바꾸어 보았다. 전체 코드와 실행과정을 유심히 살펴봐 주기 바란다.

 

 

 

위의 코드를 보면, self.speed 라는 인스턴스 변수가 __init__( ) 함수, accel( ) 함수, 그리고 showSpeed( )함수 등 3개의 함수에서 공통적으로 접근하여 읽거나 쓰고 있음을 확인할 수 있다. 실제로,  __init__( ) 함수에서 초기화되었고, accel( )함수에서 새로운 값으로 바뀌었으며, showSpeed( ) 함수에서 그 값이 참조되었음을 확인할 수 있다. 

 

또한, 실제 accel( )함수의 동작이 self.speed 값이 무엇이냐에 따라 달라진다는 것도 확인할 수 있다. 만약 현재 속도가 10이라면 20으로 바꾸고, 20이라면 30으로 바꾸고, 30이라면 40으로 바꾸는 것이 된다. 여기서, 객체들의 행위에 영향을 주는 인스턴스 변수(들)의 현재 값을 객체의 상태(state)라고 부른다. 예를 들어 설명해 보면, 현재 우리 차가 시속 30인 상태에 있다. 여기서, 우리가 accel( ) 을 하게 되면, 우리 차는 시속이 40인 상태로 바뀐다.

 

여기서, 한가지만 더 짚어보고 넘어가자. 만약 우리의 Car 객체들은 (실제로는 절대 그럴 수가 없겠지만... 혹시 자율주행 자동차가 일상화되면 이럴 수도 있을려나? ^^) 초기 속도가 임의로 초기화될 수 있다고 해 보자. 어떤 차들은 초기 속도가 10으로 태어나고, 어떤 차들은 초기 속도가 50으로 만들어지기도 한다. 즉, 인스턴스 변수의 초기화 값이 하나의 단일한 디폴트 값으로 세팅되는 것이 아니라 객체에 따라 달라진다고 해 보자. 그럴 경우에는 __init__( ) 함수에 그 값을 인자로 넘겨줄 수 있으며, 이 인자값들은 생성함수에서 제공할 수 있다. 다음의 수정된 Car 클래스 코드를 함께 보자.

 

 

위 코드에서 In[37]에서 생성자 함수가 인자를 가지고 있는 것을 확인할 수 있으며, 이 값이 __init__(  ) 함수의  positional argument 인 x 변수로 전달되어 실제 self.speed 인스턴스 변수에 저장되고 있는 것을 확인할 수 있다. In[38]에서 Car 객체를 생성하자 마자 self.speed 값을 참조했더니 초기값 100이 저장되어 있었음을 확인할 수 있다. 

 

○ 객체의 상태(state)는 객체 설계의 시발점이 된다

 

객체가 가질 수 있는 (또는, 도달할 수 있는) 상태(state)는 그 객체의 행위를 설명하는데 또는 그 객체의 행위를 설계하는데 매우 중요하게 고려된다. 해당 객체가 어떤 어떤 상태를 가질 수 있는지에 따라서 그에 따르는 동작 특성들도 다르게 설계되어야 하기 때문이다.

 

어려운 말이다ㅠ 차근 차근 같이 알아 보기로 하자~^^