점프 투 파이썬 - 클래스
Language/Python

점프 투 파이썬 - 클래스

뉴비뉴 2019. 7. 12.

과자 틀 -> 클래스

과자 틀에 의해서 만들어진 과자 -> 객체(object)

 

[객체와 인스턴스의 차이]

클래스로 만든 객체를 인스턴스라고도 한다. 그렇다면 객체와 인스턴스의 차이는 무엇일까? 이렇게 생각해 보자. a = Cookie() 이렇게 만든 a는 객체이다. 그리고 a 객체는 Cookie의 인스턴스이다. 즉 인스턴스라는 말은 특정 객체(a)가 어떤 클래스(Cookie)의 객체인지를 관계 위주로 설명할 때 사용한다. "a는 인스턴스"보다는 "a는 객체"라는 표현이 어울리며 "a는 Cookie의 객체"보다는 "a는 Cookie의 인스턴스"라는 표현이 훨씬 잘 어울린다.

 

사칙연산 클래스 만들기

클래스는 객체를 중심으로 어떤 식으로 동작하게 할것인지 미리 구상을 한 후에 생각한 것들을 하나씩 해결하면서

완성해 나가는 것이 좋다.

 

클래스 구조 만들기

>>> class FourCal:
... 	pass # pass는 아무것도 수행하지 않는 문법으로 임시로 코드를 작성할 때 주로 사용한다.

>>> a = FourCal()
>>> type(a)
<class '__main__.FourCal'> # type(a)를 통해 a 는 FourCal 클래스의 객체임을 알 수 있다.

객체에 숫자 지정할 수 있게 만들기

사용할 2개의 숫자를 a 객체에 지정해보자.

>>> a.setdata(4, 2)
위 문장을 수행하려면 다음과 같이 소스 코드를 작성해야 한다.

class FourCal:
	def setdata(self, first, second): # setdata 함수 생성/ 클래스 안에 구현된 함수는 메서드라고 부른다.
    	self.first = first
        self.second = second

a.first 를 입력하면 4가 출력되고, a.second를 입력하면 2가 출력된다.

파이썬 메서드의 첫 번째 매개변수 이름은 관례적으로 self를 사용한다.

객체를 호출할 때 호출한 객체 자신이 전달되기 때문에 self를 사용한 것이다.

ex)

def __str__(self)

   return self.title

 

[메서드의 또 다른 호출 방법]

잘 사용하지는 않지만 다음과 같이 클래스를 통해 메서드를 호출하는 것도 가능하다.

>>> a = FourCal()

>>> FourCal.setdata(a, 4, 2)

위와 같이 클래스 이름.메서드 형태로 호출할 때는 객체 a를 첫 번째 매개변수 self에 꼭 전달해 주어야 한다. 반면에 다음처럼 객체.메서드 형태로 호출할 때는 self를 반드시 생략해서 호출해야 한다.

>>> a = FourCal()

>>> a.setdata(4, 2)

>>> b = FourCal()

>>> b.setdata(3,7)

>>>print(a.first) : 4

>>>print(b.first) : 3

 

a 객체의 first 값은 b 객체의 first 값에 영향 받지 않고 원래 값을 유지하고 있음을  확인할 수 있다.

클래스로 만든 객체의 객체변수는 다른 객체의 객체변수에 상관없이 독립적인 값을 유지한다.

 

생성자(Constructor) __init__(self)

>>> a = FourCal()
>>> a.add()
#오류발생!!!!!!!!!!!!!!!!!!!!
#FourCal 클래스의 인스턴스 setdata 메서드를 수행하지 않고 add 메서드를 수행하면 오류가 발생한다.
#setdata 메서드를 수행해야 객체 a의 객체변수 first와 second가 생성되기 때문이다.

이렇게 객체에 초기값을 설정해야 할 필요가 있을 때는 setdata와 같은 메서드를 호출하여 초기값을 설정하기보다는 생성자를 구현하는 것이 안전한 방법이다. 생성자란 객체가 생성될 때 자동으로 호출되는 메서드를 의미한다.

 

파이썬 메서드 이름으로 __init__를 사용하면 이 메서드는 생성자가 된다.

def __init__(self,first,second) # __init__으로 했기 때문에 생성자로 인식되어 객체가 생성되는 시점에 자동 호출
	self.first = first
    self.second = second
class FourCal:
    def __init__(self,first,second):
        self.first = first
        self.second = second
    def setdata(self,first,second):
        self.first = first
        self.second = second
    def add(self):
        result = self.first + self.second
        return result
    def mul(self):
        result = self.first * self.second
        return result
    def sub(self):
        result = self.first - self.second
        return result
    def div(self):
        result = self.first / self.second
        return result

a = FourCal(4,3)
print(a.first)
print(a.second)

기존에 사용하던 setdata 메서드가 필요가 없다. init 메서드도 다른 메서드와 마찬가지로 첫 번째 매개변수 self 에

생성되는 객체가 자동으로 전달된다.

클래스의 상속

상속(Inheritance)이란 "물려받다"라는 뜻이다. 상속 개념을 사용하여 우리가 만든 FourCal 클래스에 a의 b제곱을 구할 수 있는 기능을 추가해 보자.

class MoreFourCal(FourCal)
	pass
    
class 클래스 이름(상속할 클래스 이름)

MoreFourCal 클래스는 FourCal 클래스를 상속했으므로 FourCal 클래스의 모든 기능을 사용할 수 있다.

class FourCal:
    def __init__(self,first,second):
        self.first = first
        self.second = second
    def add(self):
        result = self.first + self.second
        return result
    def mul(self):
        result = self.first * self.second
        return result
    def sub(self):
        result = self.first - self.second
        return result
    def div(self):
        result = self.first / self.second
        return result

class MoreFourCal(FourCal):
    pass

a = MoreFourCal(4,2)
print(a.add()) # 4+2 = 6

# 제곱 pow 메서드 생성
class MoreFourCal(FourCal):
    def pow(self):
        result = self.first ** self.second
        return result

a = MoreFourCal(4,2)
print(a.pow())

왜 상속을 해야 할까?

보통 상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용한다.

"클래스에 기능을 추가하고 싶으면 기존 클래스를 수정하면 되는데 왜 굳이 상속을 받아서 처리해야 하지?" 라는 의문이 들 수도 있다. 하지만 기존 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황이라면 상속을 사용해야 한다.

메서드 오버라이딩

>>> a = FourCal(4,0)
>>> a.div()
# 오류 발생!! 4를 0으로 나누려고 하기 때문에 ZeroDivision 오류가 발생한다.
#하지만 0으로 나눌 때 오류가 아닌 0을 돌려주도록 만들고 싶다면 어떻게 해야 할까?

# FourCal 클래스를 상속하는 SafeFourCal 클래스를 만들어보자.
class SafeFourCal(FourCal):
	def div(self):
    	if self.second == 0: # 나누는 값이 0인 경우 0을 리턴하도록 수정
        	return 0
        else:
            return self.first / self.second # 0이 아닌 경우 나누기 실행 

SafeFourCal 클래스는 FourCal 클래스에 있는 div 메서드를 동일한 이름으로 다시 작성하였다. 이러헥 부모 클래스(상속한 클래스)에 있는 메서드를 동일한 이름으로 다시 만드는 것을 메서드 오버라이딩(Overriding, 덮어쓰기) 라고 한다.

이렇게 메서드를 오버라이딩하면 부모 클래스의 메서드 대신 오버라이딩한 메서드가 호출된다.

클래스 변수

객체변수는 다른 객체들에 영향받지 않고 독립적으로 그 값을 유지한다는 점을 이미 알아보았다.

ex)

a.setdata(3,4)

b.setdata(7,3) 

이번에는 객체변수와는 성격이 다른 클래스 변수에 대해 알아보자.

>>> class Family:
...		lastname = "김" # 클래스 변수
# 클래스 변수는 클래스 안에 함수를 선언하는 것과 마찬가지로 클래스 안에 변수를 선언하여 생성한다.

>>> print(Family.lastname) #클래스이름.클래스 변수
김

클래스 변수는 객체변수와 다르게 값이 변경되면 클래스로 만든 모든 객체에 공유된다는 특징이 있다.

ex) 곰 모양의 과자 틀로 쿠키를 만들다가, 사자 모양의 과자 틀로 쿠키를 만들면 만들어지는 쿠기들이 사자 모양으로 변경되는 것과 같다고 생각하면 쉽다. 

또한 메모리까지 같은 메모리를 가르킨다.

 

댓글

💲 추천 글