이 글의 작성자는 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__ 함수 밖에 nameage 변수가 있으니까 왠지 __init__ 함수에서 값을 대입하면 nameage 변수의 값이 바뀔 것 같다. 그리고 p.name으로 접근을 하려고 해도 딱히 오류가 뜨지 않는걸로 보아, 문법적으로는 문제가 없어 보인다.

하지만 코드를 실행하면 다음과 같이 나올 것이다.

 0

nameage가 바뀌지 않은 것 같다. 뭔가 잘못된 것 같다. 여전히 nameage는 __init__ 함수의 지역 네임스페이스에 속해있는 것 같다. 그렇다면 메서드 내에서 person 클래스 안에 정의되어 있는 변수 nameage에 접근하는 방법이 있을 것이다. 어떻게 접근하면 될까?

클래스 이름을 통해 접근하면 된다. 다음과 같이 해 보자.

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의 이름과 나이가 변경된 것이다. 왜 그런걸까?

 

사실 이니셜라이저에서 대입한 nameage는 p가 가리키는 객체의 것이 아니다. person 클래스 자체가 보유하고 있는 속성인 것이다. 따라서 위와 같이 코드를 정의하면 모든 person 객체는 동일한 name을 갖게 되는 것이다. 이를 클래스 속성(class attribute)라고 부른다. 이 뒤로 r,t,y,u에 person 객체를 생성하여 대입한다고 해도, 모든 person 객체는 똑같은 name을 갖게 되는 것이다.

그렇다면 개개인의 개성을 드러내는 이름과 나이는 클래스 속성으로 쓸 수 없을 것이다. 들어갈 수 있을법한 속성이라면 사람의 분류학적 이름 정도가 있겠다. (호모 사피엔스)

 

객체(또는 인스턴스) 속성과 비교 요약하자면 다음과 같다.

  • 클래스 속성은 해당 클래스로 만들어진 모든 객체가 공유하는 속성이다. 다른 클래스 속성을 지닌 객체는 존재할 수 없다.
  • 객체 속성은 각각의 객체가 따로 가지고 있는 속성이다. 다른 객체 속성을 지닌 객체는 존재할 수 있다.

객체를 설계할 때 어떤 종류의 속성을 사용할 지는 그 속성이 지닌 특성에 따라 잘 골라야 할 것이다.

 

Categories:

Updated:

Comments