본문 바로가기

Programming Language/Python

[Python] 6. 클래스 (1) - 클래스 기본 사용법 (객체, 메서드)

반응형

 

[목차]

 

1. 클래스(Class)란?

 

2. 객체와 인스턴스(Instance)

 

3. 클래스와 메서드 만들기

 

 

  1. 클래스(Class)란?

 

클래스(Class)란, 쉽게 말해 객체(instance)를 만들어내기 위한 '틀'이다. '객체지향 프로그래밍'의 핵심이기도 하다. 

주로 붕어빵에 비유하는데, 붕어빵을 객체라고 한다면 클래스는 붕어빵 틀에 해당한다. 

즉, 동일한 무언가를 계속해서 만들어낼 수 있는 형태이며 만들어낸 객체들은 모두 고유한 성격을 가진다. 단, 객체별로 갖는 기능은 모두 동일하다.

붕어빵을 예시로 들면, 같은 틀로 만들어낸 여러 개의 붕어빵 중에서 하나를 조금 먹더라도 다른 붕어빵에는 아무 영향을 미치지 않는 것과 동일하다. 

 

클래스를 이용하는 이유는 무엇일까?

예를 들어서 2명의 학생이 있고, 각자 0부터 임의의 수를 덧셈을 해나간 결과가 필요할 때 이를 함수로 구현한다면 다음과 같다. 

student1 = 0
student2 = 0

def plus1(num):
    global student1
    student1 += num
    return student1

def plus2(num):
    global student2
    student2 += num
    return student2

print(plus1(3)) # 3
print(plus2(4)) # 4
print(plus1(5)) # 8
print(plus2(5)) # 9

 

이렇게 각 학생마다 계산을 해주는 함수를 별도로 만들어야 한다. 

하지만, 만약 학생의 수가 더 늘어나거나 또는 덧셈뿐만 아니라 뺄셈, 곱셈 등의 다른 연산도 추가가 된다면 어떨까?

작성해야 할 함수가 매우 많아지게 될 것이다. 

 

이를 클래스를 이용하면 간단하게 작성할 수 있다. (클래스를 만드는 방법에 대해서는 뒤에서 다루겠다)

class Oper:
    def __init__(self):
        self.res = 0
    
    def plus(self, num):
        self.res += num
        return self.res

student1 = Oper()
student2 = Oper()

print(student1.plus(3)) # 3
print(student2.plus(4)) # 4
print(student1.plus(5)) # 8
print(student2.plus(5)) # 9

 

이 상태로는 코드 길이 상으로 큰 차이가 없어 보이지만, 만약 학생이 10명이 된다면 이전의 방식으로는 함수를 10개를 만들어야 한다. 하지만 클래스를 이용하면 아래와 같이 이용하면 된다. 

class Oper:
    def __init__(self):
        self.res = 0
    
    def plus(self, num):
        self.res += num
        return self.res

student1 = Oper()
student2 = Oper()
student3 = Oper()
...
student10 = Oper()

 

덧셈뿐만 아니라 다른 연산 기능도 모든 학생들에게 추가해주고 싶다면 단순히 클래스에다가 함수를 하나 더 만들어주면 된다. 

 

이처럼 클래스를 이용하면 코드를 간결하게 작성할 수 있으며, 특정 기능 또는 특정 변수에만 작용하는 함수를 쉽게 유지 보수할 수 있는 장점이 있다. 

 

 

  2. 객체와 인스턴스(Instance)

 

앞에서 클래스와 객체의 관계에 대해서 간략하게 소개를 했다.

조금 더 설명하자면, 객체는 클래스에서 선언된 틀 그대로 만들어진 실체이며, 자신의 고유의 상태와 이름, 행동을 갖는다. 

객체의 속성을 나타내는 것은 멤버 변수라고 하며, 객체의 속성에 영향을 주는 기능 또는 행동을 멤버 함수 또는 메서드라고 한다. 

이전의 덧셈 예시에서 res가 객체의 속성이고, plus함수가 객체의 기능이라고 볼 수 있다. 

 

보통, 객체와 인스턴스를 크게 구분짓지 않고 사용하기도 하지만 약간의 차이점이 있다. 

인스턴스는 '클래스로 만든 객체'를 말한다. 이전에 student = Oper() 를 통해서 student를 만든 것을 예로 들면, student는 객체이다. 그리고 student 객체는 Oper의 인스턴스이다. 

인스턴스는 객체와 클래스와의 관계 위주로 설명할 때 "~~는 **의 인스턴스이다"와 같이 표현하며, 단순하게 가리킬 때는 객체라고 표현하는 게 적절하다. 

 

 

  3. 클래스와 메서드 만들기

 

1. 클래스와 메서드 생성하기

 

이제, 클래스를 어떻게 생성하는지 알아보자. 

클래스는 class라는 키워드 다음에 클래스 이름을 지정하고 콜론(:)을 붙여준다. 보통 클래스의 이름은 첫 문자를 대문자로 시작한다. 함수를 생성하는 방법과 비슷하다.

 

class 클래스명:

    [코드]

 

그리고, 메서드는 함수와 동일하게 생성한다. 단, 메서드의 첫 번째 매개변수는 반드시 self로 지정해야 한다. 여기서 self는 인스턴스 자신을 의미한다. 인스턴스 내부의 속성을 다루기 위해서 "자신의 속성을 다룬다"를 알려주기 위해서 self를 사용한다. 

 

class 클래스명:

    def 메서드명(self, ...):

        [메서드 코드]

 

객체는 '객체명 = 클래스명()'을 통해서 생성하며, 메서드는 객체 뒤에 점을 붙인 후 메서드를 호출하면 된다.

class Hello:
    def morning(self):
        print("Good Morning!")
    def night(self):
        print("Good Night!")

a = Hello()
b = Hello()
a.morning() # Good Morning!
b.night() # Good Night!

 

만약 빈 클래스를 생성하고 싶다면 함수와 동일하게 안에 pass 키워드를 넣어준다. 

class Hello:
    pass

 

2. 파이썬에서의 기본 클래스

 

 

사실 이전까지 우리는 클래스를 많이 이용해 오고 있었다. 숫자 자료형, 리스트, 딕셔너리 등도 모두 클래스이다. 

해당 변수들의 타입을 출력해보면 클래스라고 나오는 것을 알 수 있다.

예를 들어서 a = int(3)이라고 한다면, int라는 클래스에 3을 넣어서 a라는 객체를 만들어내었다는 의미이다. 

마찬가지로 list( )를 통해서 리스트를 생성하거나, 딕셔너리, 튜플 등도 모두 클래스를 이용하는 것이다. 

a = 3
print(type(a)) # <class 'int'>

b = list(range(4)) 
print(type(b)) # <class 'list'>

c = [1, 2, 3]
print(type(c)) # <class 'list'>

d = (1, 2, 3)
print(type(d)) # <class 'tuple'>

 

그렇다면 이 클래스들의 메서드는 뭘까? 우리가 자료형 별로 사용해오던 함수들이 바로 클래스의 메서드이다. 

list에서 append, count, remove 등이 모두 메서드이다. 

 

 

3. 복잡한 클래스 만들기

 

class Hello:
    def __init__(self):
        self.morn = "Good Morning!"
    def morning(self):
        print(self.morn)

a = Hello()
a.morning() # Good Morning!

 

위의 클래스를 보면 __init__이란 함수를 호출하지 않았음에도 morning 메서드를 수행하면 "Good Morning!"이 출력된다. __init__ 메서드는 클래스를 통해서 객체를 생성하는 순간에 호출되는 특별한 메서드이다. init 앞뒤로 밑줄(_)이 두 개씩 들어가 있다. 흔히 클래스의 '생성자'라고 한다. 

 

init은 initialize를 줄인 말로, 뜻 그대로 처음에 초기화를 시켜준다는 의미이다. 

따라서, 객체를 생성할 때 특정 값으로 속성을 초기화시켜주고 싶다면 __init__ 함수를 이용하면 된다. 

__init__ 메서드도 함수이기 때문에 여러 매개변수를 지정할 수 있다. 이 매개변수들로 넘겨진 인자를 초기화에 사용한다. 

아래의 예시를 통해 이해해보자.

class Person:
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age
        
    def introduce(self):
        print('이름 : %s' %self.name)
        print('성별 : %s' %self.sex)
        print('나이 : %d' %self.age)

a = Person('John', 'man', 21)

a.introduce()
# 이름 : John
# 성별 : man
# 나이 : 21

 

이렇게 객체 a를 생성할 때 __init__함수의 매개변수에 맞게 인자를 넣어주면 __init__함수에서 객체의 속성에 대해 초기화를 시켜준다. 

'John'이 self.name 변수에 할당되고, 'man'이 self.sex에, 21이 self.age에 할당된 상태로 객체가 만들어지는 것이다. 

여기서 self는 자기 자신이므로 결국 각각 a.name, a.sex, a.age에 할당되는 것이다. 

 

그렇다면 __init__함수의 매개변수는 총 4개인데 왜 3개의 인자만 넣어도 될까?

self 매개변수에는 메서드를 호출한 객체 a가 자동으로 전달되기 때문이다. 항상 첫 번째 매개변수인 self에는 자기 자신이 들어간다. 

 

이제 이를 참고하면 클래스의 장점을 더욱더 느낄 수 있다. 1명이 아니라 많은 사람들의 정보를 저장할 필요가 있다면 굳이 한 명씩 직접 이름, 성별, 나이에 해당하는 변수를 생성할 필요 없이 클래스를 이용해서 간편하게 만들 수 있고, 또 모든 사람에게 공통으로 특정 기능을 추가하고 싶다면 단순히 클래스에서만 한번 추가하면 된다. 

 

아래와 같이 구현할 수도 있다. 

class Person:
    def __init__(self, *args):
        self.name = args[0]
        self.sex = args[1]
        self.age = args[2]
    def introduce(self):
        print('이름 : %s' %self.name)
        print('성별 : %s' %self.sex)
        print('나이 : %d' %self.age)

a = Person(*['John', 'man', 21])

a.introduce()
# 이름 : John
# 성별 : man
# 나이 : 21

 

이전에 함수에서 가변 인수를 배웠다. __init__또한 함수이므로 가변 인수를 사용할 수 있다. 

물론 키워드 인수 또한 사용할 수 있다. 

 

 

4. 특정 인스턴스에만 속성 추가

 

클래스에서 선언한 속성 말고도 생성된 객체에서 자신만의 속성을 추가할 수 있다. 

class Person:
    def __init__(self, *args):
        self.name = args[0]
        self.sex = args[1]
        self.age = args[2]
    def introduce(self):
        print('이름 : %s' %self.name)
        print('성별 : %s' %self.sex)
        print('나이 : %d' %self.age)

a = Person('John', 'man', 21)
b = Person('minsu', 'man', 21)

a.job = 'singer'

print(a.job) # singer
print(b.job) # AttributeError: 'Person' object has no attribute 'job'

 

a라는 객체에 job이라는 속성을 추가하였다. a.job을 출력하면 singer가 출력되지만, b.job을 출력하면 해당 객체에는 job이라는 변수가 없다는 에러가 발생한다.

즉, 특정 인스턴스에 속성을 추가하더라도, 동일한 객체로부터 만들어진 다른 인스턴스에는 영향을 미치지 않는다는 것을 알 수 있다.  

반면 Person.job = 'singer'로 추가하면 Person 클래스로 생성된 모든 인스턴스에 job 속성이 추가된다.

 

 

5. 비공개 속성, 메서드 (private attribute)

 

이전까지 만든 속성이나 메서드는 모두 클래스 밖에서 접근할 수 있었다. 하지만 특정 인스턴스만 값이 다른 경우가 존재하면 안 되는 경우가 있을 수도 있다. 이러한 경우에 클래스 밖에서는 접근할 수 없도록 하는 기능이 비공개 속성(private attribute)이다. 

 

비공개 속성은 속성 이름 앞에 밑줄(_) 두 개가 붙어야 한다. 

class Person:
    def __init__(self, name, sex, age, height):
        self.name = name
        self.sex = sex
        self.age = age
        self.__height = height
a = Person('John', 'man', 21, 180)
a.age += 1
print(a.age) # 22
a.__height += 2 # AttributeError: 'Person' object has no attribute '__height'

 

이처럼 age 속성은 1을 증가시켜줄 수 있지만, __height 속성을 변화시키려고 하면 에러가 발생한다. 클래스 밖에서 변경시킬 수 없기 때문이다. 

 

메서드 또한 마찬가지이다. 

class Person:
    def __init__(self, name, sex, age, height):
        self.name = name
        self.sex = sex
        self.age = age
        self.__height = height
        
    def __hi(self):
        print("Say hi!")
        
    def hello(self):
        self.__hi()
   
a = Person('John', 'man', 21, 180)

a.hello() # Say hi!
a.__hi() # AttributeError: 'Person' object has no attribute '__hi'

 

 

PC로 보시는 것을 권장합니다. 

피드백은 언제나 환영입니다. 댓글로 달아주세요 ^-^

 

반응형