파이썬과 클래스 (2)
이 글의 작성자는 C/C++ 프로그래밍을 하던 사람이다. 이 글은 Python을 복습하며 작성하는 글이니, 부족한 부분이 있으면 얼마든지 피드백을 주시기 바란다.
클래스의 네임스페이스
객체의 속성을 추가하는 방법에 대해 이야기할 때, self.를 꼭 붙이라는 이야기를 한 적이 있었다. 그런데 self.을 붙이지 않아도 동작을 한다고 이야기 했었다. 그러면 self.를 붙이지 않으면 뭐가 다른걸까? 한 번 테스트해보자.
class person:
def __init__(self, _name, _age):
name = _name
age = _age
p = person('jack', 23)
여기까지 코드를 작성하고 실행하면 잘 된다. 하지만 마지막에 이 코드를 추가하면 오류가 발생할 것이다.
print(p.name, p.age)
AttributeError: 'person' object has no attribute 'name'
출력된 텍스트를 읽어보면, ‘person 객체는 ‘name’이란 속성이 없어!’ 라는 이야기를 하고 있다. self.을 떼버리니, name
은 없는 속성이 되어버렸다. 하지만 분명히 name
이라는 속성은 정의를 했었다. 그러면 어디로 간걸까?
결론부터 말하자면, 사라졌다.
언제 사라지느냐? __init__ 함수가 끝나자마자 사라졌다.
왜 사라졌느냐? __init__ 함수의 지역 네임스페이스를 벗어났으니까.
함수가 클래스 안에 있다고 하더라도 특별할 건 없다. 클래스 안에 있는 함수, 즉 메서드의 코드도 결국은 지역 네임스페이스를 가진다. 그래서 메서드의 매개 변수로 self가 항상 따라오는 이유가 바로 이것이다. 객체의 속성을 정의하기 위해선 self가 반드시 필요하다.
클래스 속성
__init__ 함수 안에 변수를 선언하면 __init__함수의 지역 네임스페이스로 들어간다는 것은 알았다. 그런데 변수를 선언할 수 있을 것 같은 곳이 한 군데 더 있다. 바로 person 클래스 안이다. 함수와 동등한 위치에 변수를 선언하면 어떻게 될까?
class person:
name = str()
age = int()
위의 클래스는 오류가 없는 클래스이다. 즉, 변수를 저기에다 선언해도 괜찮다는 것을 의미한다. 그러면 __init__ 함수에서 저 변수들에 값을 대입하면 되는걸까? 다음과 같이 코드를 작성해보자.
class person:
name = str()
age = int()
def __init__(self, _name, _age):
name = _name
age = _age
p = person('jack', 23)
print(p.name, p.age)
__init__ 함수 밖에 name
과 age
변수가 있으니까 왠지 __init__ 함수에서 값을 대입하면 name
과 age
변수의 값이 바뀔 것 같다. 그리고 p.name
으로 접근을 하려고 해도 딱히 오류가 뜨지 않는걸로 보아, 문법적으로는 문제가 없어 보인다.
하지만 코드를 실행하면 다음과 같이 나올 것이다.
0
name
과 age
가 바뀌지 않은 것 같다. 뭔가 잘못된 것 같다. 여전히 name
과 age
는 __init__ 함수의 지역 네임스페이스에 속해있는 것 같다. 그렇다면 메서드 내에서 person
클래스 안에 정의되어 있는 변수 name
과 age
에 접근하는 방법이 있을 것이다. 어떻게 접근하면 될까?
클래스 이름을 통해 접근하면 된다. 다음과 같이 해 보자.
class person:
name = str()
age = int()
def __init__(self, _name, _age):
person.name = _name
person.age = _age
p = person('jack', 23)
print(p.name, p.age)
위의 코드를 실행하면, 이니셜라이저로 전달한 _name
과 _age
가 출력되는 것을 확인할 수 있을 것이다.
jack 23
그러면 또 하나의 의문이 들 수 있다. 그러면 self.name으로 정의한 속성과 person
클래스 내부에 정의한 속성의 차이는 무엇일까?
객체를 하나 더 만들어보자. 그러면 차이를 확인할 수 있다.
p = person('jack', 23)
print(p.name, p.age)
q = person('mike', 25)
print(p.name, p.age)
위의 코드를 실행하면 다음과 같이 출력될 것이다.
jack 23
mike 25
얼핏 보면 새로 생성한 객체에 들어가는 이름은 ‘mike’고, 나이로 25가 들어갔으니 맞는 것처럼 보인다. 하지만 마지막의 print()
함수를 잘 보라. 지금 출력하고 있는 것은 새로 생성한 q가 아닌, 처음에 생성했던 p의 이름과 나이를 출력하고 있다! p에 대해서는 건드린 것이 아무것도 없는데, 뜬금없이 p의 이름과 나이가 변경된 것이다. 왜 그런걸까?
사실 이니셜라이저에서 대입한 name
과 age
는 p가 가리키는 객체의 것이 아니다. person 클래스 자체가 보유하고 있는 속성인 것이다. 따라서 위와 같이 코드를 정의하면 모든 person 객체는 동일한 name을 갖게 되는 것이다. 이를 클래스 속성(class attribute)라고 부른다. 이 뒤로 r,t,y,u에 person 객체를 생성하여 대입한다고 해도, 모든 person 객체는 똑같은 name
을 갖게 되는 것이다.
그렇다면 개개인의 개성을 드러내는 이름과 나이는 클래스 속성으로 쓸 수 없을 것이다. 들어갈 수 있을법한 속성이라면 사람의 분류학적 이름 정도가 있겠다. (호모 사피엔스)
객체(또는 인스턴스) 속성과 비교 요약하자면 다음과 같다.
- 클래스 속성은 해당 클래스로 만들어진 모든 객체가 공유하는 속성이다. 다른 클래스 속성을 지닌 객체는 존재할 수 없다.
- 객체 속성은 각각의 객체가 따로 가지고 있는 속성이다. 다른 객체 속성을 지닌 객체는 존재할 수 있다.
객체를 설계할 때 어떤 종류의 속성을 사용할 지는 그 속성이 지닌 특성에 따라 잘 골라야 할 것이다.
Comments