공부이야기

05-1 파이썬 프로그래밍의 핵심, 클래스

프로필

2016. 7. 27. 17:00

이웃추가
https://wikidocs.net/28
 
05-1 파이썬 프로그래밍의 핵심, 클래스

초보 개발자들에게 클래스(class)는 넘기 힘든 장벽과도 같은 존재이다. 독자들 중에도 클래스라는 단어를 처음 접하는 이들이 있을 것이다. 여기서는 클래스가 왜 필요한지, 도대체 클래스라는 것은 무엇인지 아주 기초적인 것부터 차근차근 함께 알아보자.

클래스는 도대체 왜 필요한가?

가장 많이 사용하는 프로그래밍 언어 중 하나인 C 언어에는 클래스가 없다. 이 말은 굳이 클래스 없이도 프로그램을 충분히 만들 수 있다는 말과도 같다. 하지만 요즘 새로이 등장하는 언어들은 모두 클래스를 포함하고 있다. 그렇다면 도대체 이 클래스라는 것은 왜 필요해졌을까?

예제를 통해 한번 생각해 보자.

여러분 모두 계산기를 사용해 봤을 것이다. 계산기에 3이라는 숫자를 입력하고 + 기호를 입력한 후 4를 입력하면 결과값으로 7을 보여준다. 다시 한 번 + 기호를 입력한 후 3을 입력하면 기존 결과값 7에 3을 더해 10을 보여준다. 즉, 계산기는 이전에 계산된 결과값을 항상 메모리 어딘가에 저장하고 있어야 한다.

(※ 계산기는 이전에 계산된 결과값을 기억하고 있어야 한다.)

이런 내용을 우리가 앞서 익힌 함수를 이용해 구현해 보자. 계산기의 "더하기" 기능을 구현한 파이썬 코드는 다음과 같다.

result = 0 def adder(num): global result result += num return result print(adder(3)) print(adder(4))

(※ adder 함수는 입력 인수로 num을 받으면 이전에 계산된 결과값에 더한 후 출력하는 함수이다.)

이전에 계산된 결과값을 유지하기 위해서 result라는 전역 변수(global)를 사용했다. 실행하면 예상한 대로 다음과 같은 결과값이 출력된다.

3 7

그런데 만약 한 프로그램에서 2개의 계산기가 필요한 상황이 발생하면 어떻게 해야 할까? 각각의 계산기는 각각의 결과값을 유지해야 하기 때문에 위와 같이 adder 함수 하나만으로는결과값을 따로 유지할 수 없다.

이런 상황을 해결하려면 다음과 같이 함수를 각각 따로 만들어야 한다.

result1 = 0 result2 = 0 def adder1(num): global result1 result1 += num return result1 def adder2(num): global result2 result2 += num return result2 print(adder1(3)) print(adder1(4)) print(adder2(3)) print(adder2(7))

똑같은 일을 하는 adder1과 adder2라는 함수가 만들어졌고 각각의 함수에서 계산된 결과값을 유지하면서 저장하기 위한 전역 변수 result1, result2가 필요하게 되었다.

결과값은 다음과 같이 의도한 대로 출력된다.

3 7 3 10

계산기 1의 결과값이 계산기 2에 아무런 영향을 끼치지 않음을 확인할 수 있다. 하지만 계산기가 3개, 5개, 10개로 점점 더 많이 필요해진다면 어떻게 해야 할 것인가? 그때마다 전역변수와 함수를 추가할 것인가?

아직 클래스에 대해서 배우진 않았지만 위와 같은 경우 클래스를 이용하면 다음과 같이 간단하게 해결할 수 있다.

(※ 아래 예시 클래스를 아직은 이해하지 못해도 좋다. 곧 자세하게 배울 것이다. 여기서는 클래스를 개념적으로만 이해하면 된다.)

class Calculator: def __init__(self): self.result = 0 def adder(self, num): self.result += num return self.result cal1 = Calculator() cal2 = Calculator() print(cal1.adder(3)) print(cal1.adder(4)) print(cal2.adder(3)) print(cal2.adder(7))

실행하면 함수 2개를 사용했을 때와 동일한 결과가 출력된다.

3 7 3 10

Calculator 클래스로 만들어진 cal1, cal2라는 별개의 계산기(파이썬에서는 이것을 인스턴스라고 한다)가 각각의 역할을 수행한다. 그리고 계산기(cal1, cal2)의 결과값 역시 다른 계산기의 결과값과 상관없이 독립적인 결과값을 유지한다. 클래스를 이용하면 계산기의 개수가 늘어나더라도 인스턴스를 생성하기만 하면 되기 때문에 함수를 사용하는 경우와 달리 매우 간단해진다.

클래스의 이점은 단순히 이것만이 아니다. 하지만 이것 하나만으로도 "도대체 왜 클래스가 필요한 것일까?"라는 근본적인 물음에 대한 해답이 되었을 것이다. 이제부터 본격적으로 파이썬 클래스에 대해서 알아보도록 하자.

클래스 개념 잡기

어린 시절 뽑기를 해본 적이 있다면 다음 그림과 비슷한 모양의 뽑기 틀을 본 기억이 있을 것이다. 뽑기 아저씨가 뽑기를 불에 달군 후 다음 그림과 비슷한 모양을 찍어 주고, 그 모양대로 깨뜨리지 않고 만들어 오면 뽑기 한 개를 더 해주었다.

이 절에서 설명할 클래스라는 것이 마치 뽑기의 틀(별 모양, 하트 모양)과 비슷하다. 별 모양의 틀(클래스)로 찍으면 별 모양의 뽑기(인스턴스)가 생성되고 하트 모양의 틀(클래스)로 찍으면 하트 모양의 뽑기(인스턴스)가 나오는 것이다.

클래스란 똑같은 무엇인가를 계속해서 만들어낼 수 있는 설계 도면 같은 것이고(뽑기 틀), 인스턴스란 클래스에 의해서 만들어진 피조물(별 또는 하트가 찍힌 뽑기)을 뜻한다.

다음은 파이썬 클래스의 가장 간단한 예이다.

class Simple: pass

위의 클래스는 아무런 기능도 갖고 있지 않은 껍질뿐인 클래스이다. 하지만 이렇게 껍질뿐인 클래스도 인스턴스라는 것을 생성하는 기능은 가지고 있다. "뽑기 틀"로 "뽑기"를 만드는 것처럼 말이다.

(※ 인스턴스(Instance)와 객체는 같은 말이다. 클래스에 의해서 생성된 객체를 인스턴스라고 부른다.)

인스턴스는 클래스에 의해서 만들어진 객체로, 1개의 클래스는 무수히 많은 인스턴스를 만들어낼 수 있다. 위에서 만든 Simple 클래스의 인스턴스를 만드는 방법은 다음과 같다.

a = Simple()

Simple()의 결과값을 돌려받은 a가 바로 인스턴스이다. 마치 함수를 사용해서 그 결과값을 돌려받는 모습과 비슷하다.

 

 

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

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

 

 

이야기 형식으로 클래스 기초 쌓기

지금부터는 클래스의 개념을 아직 제대로 이해하지 못한 독자들을 위해 설명한다. 아주 쉽게 설명하려고 노력하였고 최대한 클래스의 핵심 개념에 가까이 다가갈 수 있도록 배려하였다. 클래스에 대해서 잘 아는 독자라도 재미있게 한 번 읽어 보기 바란다.

클래스 변수

다음의 클래스를 보자.

>>> class Service: ... secret = "영구는 배꼽이 두 개다."

위의 클래스 이름은 Service이다. 우리는 이 Service 클래스를 어떤 유용한 정보를 제공해 주는 한 인터넷 서비스 제공 업체라고 가정하자. 이 인터넷 업체는 가입한 고객에게만 유용한 정보를 제공한다(이 업체는 "영구는 배꼽이 두 개다"라는 비밀 정보를 가지고 있다). 자, 그렇다면 가입을 해야만 이 인터넷 업체가 가지고 있는 정보를 얻을 수 있을 것이다.

가입하는 방법은 다음과 같다.

>>> pey = Service()

위와 같이 하면 pey라는 아이디로 인터넷 서비스 업체인 Service 클래스를 이용할 수 있다. 위의 Service 클래스는 마음이 좋아서 돈도 필요 없고 비밀번호도 필요 없다고 한다.

이제 pey라는 아이디로 위 서비스 업체가 제공하는 정보를 얻어내자.

>>> pey.secret "영구는 배꼽이 두 개다"

아이디 이름에다가 서비스 업체가 제공하는 secret이라는 변수를 '.'(도트 연산자)를 이용해서 호출하였더니 실로 어마어마한 정보를 얻을 수 있었다.

클래스 함수

Service라는 이름의 이 인터넷 서비스 제공 업체는 생긴 지 얼마 되지 않은 벤처 기업이라서 이와 같이 단 한 가지 정보만을 제공하고 있었다. 제공해 주는 정보의 양이 너무 적은 듯하여 가입한 사람들을 대상으로 어떤 정보를 더 제공하면 좋을지 설문 조사를 하였더니 모두들 더하기를 잘하지 못한다고 대답했다. 그래서 이 서비스 업체는 더하기를 해주는 서비스를 제공하기로 마음을 먹었다.

두 수를 더하는 서비스를 제공해 주기 위해서 이 서비스 업체는 다음과 같이 클래스를 업그레이드했다.

>>> class Service: ... secret = "영구는 배꼽이 두 개다" # 유용한 정보 ... def sum(self, a, b): # 더하기 서비스 ... result = a + b ... print("%s + %s = %s입니다." % (a, b, result)) ... >>>

클래스가 업그레이드되어 이제 업체에 가입한 모든 사람들이 더하기 서비스를 제공받을 수 있게 되었다. 이 업체의 서비스를 이용하는 방법은 다음과 같다.

회원으로 가입되어 있지 않다면 먼저 서비스 업체에 가입을 해서 아이디를 받는다.

>>> pey = Service()

다음에 더하기 서비스를 이용한다. secret변수를 호출할 때와 마찬가지로 아이디에 도트 연산자를 이용하여 sum함수를 호출하도록 한다.

>>> pey.sum(1,1) 1 + 1 = 2입니다.

더하기의 결과값이 정상적으로 출력되었다.

self 간단히 살펴보기

그렇다면 이번에는 서비스 업체의 입장에서 생각해 보자. 서비스 업체는 오직 가입한 사람들에게만 서비스를 제공하고 싶어 한다. 이를 위해 그들은 더하기 서비스에 가입했는지 여부를 확인하기 위한 장치를 추가했다. 이전에 보았던 더하기를 제공해 주는 함수를 다시 보면 다음과 같다.

... def sum(self, a, b): ... result = a + b ... print("%s + %s = %s입니다." % (a, b, result))

누군가 이 서비스 업체의 더하기 서비스를 이용하고 싶다고 요청했을 때 이 사람이 가입을 한 사람인지 안 한 사람인지 가리기 위해 위처럼 sum이라는 함수의 첫 번째 인수값으로 self라는 것을 사용했다. 어떤 사람이 다음처럼 더하기 서비스를 이용하려 한다고 생각해 보자.

>>> pey = Service() >>> pey.sum(1, 1)

이렇게 하면 pey라는 아이디를 가진 사람이 이 서비스 업체의 sum이라는 서비스를 이용하겠다고 요청한다는 뜻이다. 위와 같이 했을 때 업체가 제공하는 더하기 서비스(sum 함수)는 다음과 같이 생각한다.

"어, 누군가 더하기 서비스를 해달라고 하네? 그럼 서비스를 해주기 전에 먼저 이 사람이 가입을 한 사람인지 아닌지 판단해야겠군. 자 그럼 첫 번째 입력값으로 뭐가 들어오는지 보자. 음...... pey라는 아이디를 가진 사람이군. 가입한 사람이 맞네. 서비스를 제공해 주자!"

앞에서 봤듯이 서비스 업체는 sum 함수의 첫 번째 입력값을 통해 가입 여부를 판단했다. 다시 sum 함수를 보자.

... def sum(self, a, b): ... result = a + b ... print("%s + %s = %s입니다." % (a, b, result))

위의 sum 함수는 첫 번째 입력값으로 self라는 것을 받고 두 번째, 세 번째 입력값으로 더할 숫자를 받는다. 입력으로 받는 입력 인수의 개수가 총 3개인 것이다.

따라서 pey라는 아이디를 가진 사람은 다음처럼 sum 함수를 사용해야 할 것이다.

pey.sum(pey, 1, 1)

sum 함수는 첫 번째 입력값을 가지고 가입한 사람인지 아닌지를 판단한다. 따라서 첫 번째 입력 인수로 pey라는 아이디를 주면 sum 함수는 pey라는 아이디가 이미 가입되어 있는 것을 확인한 후 서비스를 제공해 줄 것이다.

그런데 이전에 sum 함수를 호출할 때는 아래 문장처럼 pey를 중복해서 사용하지 않아도 문제가 없었다. 왜 그럴까?

>>> pey.sum(1, 1)

pey.sum(1, 1)이라는 호출이 발생하면 sum함수의 첫번째 인수인 self는 호출 시 이용했던 인스턴스(즉, pey라는 아이디)로 바뀌게 된다. 그래서 pey.sum(pey, 1, 1)이 아닌 pey.sum(1, 1)이 가능하게 된 것이다. 앞의 방법보다는 뒤의 방법이 이용하기도 쉽고 보기도 쉽지 않은가?

(※ pey.sum(1, 1)은 Service.sum(pey, 1, 1)처럼 사용해도 동일한 결과를 얻는다.)

 

 

[self는 왜 필요할까?]

self라는 변수를 클래스 함수의 첫 번째 인수로 받아야 한다는 것은 파이썬만의 (좋지 않은?) 특징이다. 굳이 왜 "self"가 필요했는지는 파이썬 언어가 개발된 원리를 깊이 들여다보아야만 알 수 있다. 우리는 파이썬 언어 개발자가 아니니 단순하게 생각하자. 즉, 클래스 내 함수의 첫 번째 인수는 무조건 self로 사용해야 인스턴스의 함수로 사용할 수 있다고만 알아두자.

 

 

self 제대로 알기

시간이 흘러 더하기 서비스를 계속 제공받던 사람들은 서비스 업체에 뭔가 색다른 서비스를 요구하기 시작했다. 그 요구는 더하기 서비스를 제공할 때 "홍길동 님 1 + 2 = 3 입니다." 처럼 홍길동이라는 자신의 이름을 넣어 달라는 것이었다.

인터넷 서비스 업체인 Service는 "이름 출력 서비스"를 제공해 주기로 마음을 먹었다. 그래서 다음과 같이 또 Service 클래스를 업그레이드하게 되었다.

사용자에게 이름을 입력받아서 더하기 서비스를 제공할 때 앞부분에 그 이름을 넣어 주도록 다음과 같이 변경되었다.

>>> class Service: ... secret = "영구는 배꼽이 두 개다" ... def setname(self, name): ... self.name = name ... def sum(self, a, b): ... result = a + b ... print("%s님 %s + %s = %s입니다." % (self.name, a, b, result)) ... >>>

그리고 바뀐 점과 "이름 출력 서비스"의 사용법에 대해서 가입한 사람들에게 알려주었다. 그래서 사람들은 다음처럼 서비스를 이용할 수 있게 되었다.

먼저 서비스 업체에 가입을 해서 pey라는 아이디를 얻는다.

>>> pey = Service()

다음으로 pey라는 아이디를 가진 사람이 자신의 이름은 홍길동임을 서비스 업체에 알려준다.

>>> pey.setname("홍길동")

더하기 서비스를 이용한다.

>>> pey.sum(1, 1) 홍길동님 1 + 1 = 2입니다.

이제 서비스 업체의 입장에서 다시 한 번 생각해 보자.

우선 이름을 입력받은 함수 setname을 살펴보자.

... def setname(self, name): ... self.name = name

아래처럼 pey라는 아이디를 부여받은 사람이 있다.

>>> pey = Service()

이 사람이 자신의 이름을 설정하겠다는 요구를 다음과 같이 했다.

>>> pey.setname("홍길동")

위와 같은 상황이 발생했을 때 서비스 제공 업체의 setname 함수는 다음과 같이 생각한다.

"pey라는 아이디를 가진 사람이 자신의 이름을 홍길동으로 설정하려고 하는구나. 그렇다면 앞으로 pey라는 아이디로 접근하면 이 사람의 이름이 홍길동이라는 것을 잊지 말아야겠다."

위와 같이 pey라는 아이디와 홍길동이라는 이름을 연결해 주는 것이 바로 self이다. setname함수가 실행되는 순서는 다음과 같다.

1. pey라는 아이디를 가진 사람이 "홍길동"이라는 이름을 setname 함수에 입력으로 준다.

>>> pey.setname("홍길동")

2. 그러면 다음의 문장이 수행된다.

self.name = name

3. self는 setname 함수의 첫 번째 입력값으로 pey라는 아이디를 받게 되므로 다음과 같이 바뀔 것이다.

pey.name = name

4. name은 setname 함수의 두 번째로 입력받은 "홍길동"이라는 값이므로 위의 문장은 다시 다음과 같이 바뀔 것이다.

pey.name = "홍길동"

위 코드의 의미는 pey라는 아이디를 가진 사람의 이름은 이제부터 항상 홍길동이라는 것이다. 이제 아이디 pey에 그 사람의 이름을 부여하는 과정이 끝났다.

다음으로 "홍길동님 1 + 1 = 2 입니다." 라는 서비스를 제공하기 위해 변경된 더하기 함수를 보자.

... def sum(self, a, b): ... result = a + b ... print("%s님 %s + %s = %s입니다." % (self.name, a, b, result))

"1 + 1 = 2 입니다."라는 서비스만 제공했던 이전의 sum 함수와 비교해 보면 다른 점은 단 하나, self.name이라는 것을 출력 문자열에 삽입한 것뿐이다. 그렇다면 self.name은 무엇일까? 이것을 설명하기 전에 sum 함수를 이용하기까지의 과정을 다시 한 번 살펴보자.

pey라는 이름의 아이디를 가진 사람이 자신의 이름을 "홍길동"이라고 설정한 후에 sum 함수를 다음과 같이 쓰려고 요청했다고 하자.

>>> pey = Service() >>> pey.setname("홍길동") >>> pey.sum(1, 1)

이때 서비스 업체의 sum 함수는 다음과 같이 생각한다.

"pey라는 아이디를 가진 사람이 더하기 서비스를 이용하려 하는군. 가입한 사람이 맞군. 아, 그리고 이 사람의 이름은 어디 보자 '홍길동'이군, 이름을 앞에 넣어 준 다음 결과값을 돌려주자."

여기서 우리가 기억해야 할 사항은 "sum 함수가 어떻게 pey라는 아이디를 가진 사람의 이름을 알아내게 되었을까?"이다. 위에서 배운 것을 다시 한 번 생각해 보자.

먼저 setname 함수에 의해서 "홍길동"이라는 이름을 설정해 주었기 때문에 pey.name이 "홍길동"이라는 값을 갖게 된다는 사실을 이미 알아보았다. 따라서 sum 함수에서도 self.name은 pey.name으로 치환된다. 바로 이 때문에 sum 함수는 pey라는 아이디를 가진 사람의 이름을 알아채게 되는 것이다.

(※ self는 Service에 의해서 생성된 인스턴스(예: pey)를 지칭한다는 사실을 잊지 말자.)

__init__ 이란 무엇인가?

자, 이제 또 시간이 흘러 가입자수가 많아지게 되었다. 서비스 업체가 아주 친절하게 더하기 서비스를 제공해 주었기 때문이다. 그런데 가끔 오류가 발생한다고 항의 전화가 빗발치듯 쏟아진다. 그 사람들의 말을 들어보면 다음과 같았다.

>>> babo = Service() >>> babo.sum(1, 1)

위와 같이 입력하면 자꾸 오류가 발생한다는 것이다. 그 때마다 서비스 업체에서는 babo.setname("나바보")와 같은, 이름을 넣어 주는 과정이 빠졌기 때문에 오류가 발생한다는 것이라고 골백 번 얘기를 하지만 항상 이런 실수를 하는 사람들로부터 항의 전화가 와서 여간 귀찮은 게 아니었다.

그래서 다음과 같은 아이디어를 생각해냈다. 지금까지는 사람들이 서비스에 가입할 때 별다른 과정 없이 바로 아이디를 부여해 주는 방식이었다. 그런데 만약 아이디를 부여해 줄 때 그 사람의 이름을 입력받아야만 아이디를 부여해 주는 방식으로 바꾸면 babo.setname("나바보")와 같은 과정을 생략할 수 있을 것이란 생각이었다.

위와 같이 서비스하기 위한 방법을 찾던 중 서비스 업체의 실력자 한 사람이 __init__이라는 함수를 이용하자고 제의를 했고 그 방법은 다음과 같았다.

>>> class Service: ... secret = "영구는 배꼽이 두 개다" ... def __init__(self, name): ... self.name = name ... def sum(self, a, b): ... result = a + b ... print("%s님 %s + %s = %s입니다." % (self.name, a, b, result)) ... >>>

위의 Service 클래스를 이전의 클래스와 비교해 보면 바뀐 부분은 딱 한 가지이다. 바로 setname 함수의 이름인 setname이 __init__으로 바뀐 것이다. 클래스에서 이 __init__이라는 함수는 특별한 의미를 갖는다. 그 의미는 다음과 같다.

"인스턴스를 만들 때 항상 실행된다."

즉, 아이디를 부여받을 때 항상 실행된다는 말이다. 따라서 이제는 이 서비스 업체에 가입하려면 다음처럼 입력해야 한다.

>>> pey = Service("홍길동")

이전에는 pey = Service()라고만 입력하면 되었지만 이제는 __init__ 함수 때문에 pey = Service("홍길동")처럼 아이디를 부여받을 때 이름까지 함께 입력해야 한다.

이전에 서비스를 이용하기 위해 사용했던 방식은 다음과 같다.

>>> pey = Service() >>> pey.setname("홍길동") >>> pey.sum(1, 1)

위의 코드를 __init__ 함수를 이용하면 다음처럼 간략하게 작성할 수 있다.

>>> pey = Service("홍길동") >>> pey.sum(1, 1)

서비스 방법을 이렇게 변경하고 난 후 빗발치던 항의 전화도 멈추게 되었고 세 번 입력하던 것을 두 번만 입력하면 되니 모두들 기뻐하였다.

지금까지 인스턴스와 self, __init__ 함수의 의미를 설명하기 위해 이야기 형식으로 클래스에 대해서 알아보았다. 위의 내용은 비록 클래스에 대한 정확한 설명은 아니지만 초보자가 인스턴스와 self의 의미, 그리고 __init__ 함수에 대해서 조금 더 쉽게 접근할 수 있었을 것이다. 위에서 살펴본 pey = Service()로 해서 생성된 pey를 아이디라고 하였는데, 이것이 바로 인스턴스라고 불린다는 것을 잊지 말자.

이제 위에서 살펴보았던 기초적인 사항을 바탕으로 클래스에 대해서 자세하게 알아보자.

클래스 자세히 알기

클래스란 인스턴스(Instance)를 만들어내는 공장과도 같다. 이 인스턴스를 어떻게 사용할 수 있는지를 알려면 클래스의 구조를 보면 된다. 즉, 클래스는 해당 인스턴스의 청사진(설계도)이라고 할 수 있다. 사실 지금껏 알아온 자료형, 제어문, 함수만으로도 우리가 원하는 프로그램을 작성하는 데는 문제가 없다. 하지만 클래스를 이용하면 보다 우아하게 프로그램을 만들 수 있다.

이제부터 파이썬 프로그래밍의 가장 중심이 되는 클래스에 대해서 자세히 알아보기로 하자. 잘 이해가 되지 않더라도 낙심하지는 말자. 파이썬에 익숙해지면 반드시 쉽게 이해할 수 있게 될 것이다. 여러 가지 클래스를 만들어 보면서 클래스에 대해 자세히 알아보자.

클래스의 구조

클래스는 기본적으로 다음과 같은 구조를 가지고 있다.

class 클래스이름[(상속 클래스명)]: <클래스 변수 1> <클래스 변수 2> ... def 클래스함수1(self[, 인수1, 인수2,,,]): <수행할 문장 1> <수행할 문장 2> ... def 클래스함수2(self[, 인수1, 인수2,,,]): <수행할 문장1> <수행할 문장2> ... ...

위에서 봤듯이 class라는 키워드는 클래스를 만들 때 사용되는 예약어이고 그 바로 뒤에 클래스 이름을 입력한다. 클래스 이름 뒤에 상속할 클래스가 있다면 괄호() 안에 상속할 클래스 이름을 입력한다. 클래스 내부에는 클래스 변수와 클래스 함수들이 있다.

클래스가 무엇인지 아직 감이 오지 않더라도 걱정하지 말고 다음의 예를 보며 차근차근 이해 해 보자.

사칙연산 클래스 만들기

사칙연산을 쉽게 해주는 클래스를 만들어 보자. 사칙연산은 더하기, 빼기, 나누기, 곱하기를 말한다.

클래스를 어떻게 만들지 먼저 구상하기

클래스는 무작정 만들기 보다는 클래스에 의해서 만들어진 객체를 중심으로 어떤 식으로 동작하게 할 것인지 미리 구상을 한 후에 생각했던 것들을 하나씩 해결하면서 완성해 나가는 것이 좋다.

사칙연산을 가능하게 하는 FourCal이라는 클래스가 다음처럼 동작한다고 가정해 보자.

먼저 a = FourCal()처럼 입력해서 a라는 객체를 만든다.

>>> a = FourCal()

그런 다음 a.setdata(4, 2)처럼 입력해서 4와 2라는 숫자를 a에 지정해 주고

>>> a.setdata(4, 2)

a.sum()을 수행하면 두 수를 합한 결과(4 + 2)를 돌려주고

>>> print(a.sum()) 6

a.mul()을 수행하면 두 수를 곱한 결과(4 * 2)를 돌려주고

>>> print(a.mul()) 8

a.sub()를 수행하면 두 수를 뺀 결과(4 - 2)를 돌려주고

>>> print(a.sub()) 2

a.div()를 수행하면 두 수를 나눈 결과(4 / 2)를 돌려준다.

>>> print(a.div()) 2

이렇게 동작하는 FourCal 클래스를 만드는 것이 바로 우리의 목표이다.

클래스 구조 만들기

자, 그렇다면 지금부터는 앞에서 구상했던 것처럼 동작하는 클래스를 만들어 보자. 제일 먼저 할 일은 a = FourCal()처럼 인스턴스를 만들 수 있게 하는 것이다. 일단은 아무 기능이 없어도 되기 때문에 만드는 것은 매우 간단하다. 다음을 따라 해보자.

>>> class FourCal: ... pass ... >>>

우선 대화형 인터프리터에서 pass란 문장만을 포함한 FourCal 클래스를 만든다. 현재 상태에서 FourCal 클래스는 아무런 변수나 함수도 포함하지 않지만 우리가 원하는 객체 a를 만들 수 있는 기능은 가지고 있다. 확인해 보자.

(※ pass는 아무것도 수행하지 않는 문법이다. 임시로 코드를 작성할 때 주로 사용한다.)

>>> a = FourCal() >>> type(a) <class '__main__.FourCal'>

위와 같이 a = FourCal()로 a라는 객체를 먼저 만들고 그 다음에 type(a)로 a라는 객체가 어떤 타입인지 알아보았다. 역시 객체 a가 FourCal 클래스의 인스턴스임을 알 수 있다.

(※ type 함수는 파이썬이 자체적으로 가지고 있는 내장 함수로 객체의 타입을 출력한다.)

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

하지만 생성된 객체 a는 아직 아무런 기능도 하지 못한다. 이제 더하기, 나누기, 곱하기, 빼기등의 기능을 하는 객체를 만들어야 한다. 그런데 이러한 기능을 갖춘 객체를 만들려면 우선적으로 a라는 객체에 사칙연산을 할 때 사용할 2개의 숫자를 먼저 알려주어야 한다. 다음과 같이 연산을 수행할 대상(4, 2)을 객체에 지정할 수 있게 만들어 보자.

>>> a.setdata(4, 2)

위의 문장이 수행되려면 다음과 같이 소스 코드를 작성해야 한다.

>>> class FourCal: ... def setdata(self, first, second): ... self.first = first ... self.second = second ... >>>

이전에 만들었던 FourCal 클래스에서 pass라는 문장을 삭제하고 setdata라는 함수를 만들었다. 클래스내의 함수를 다른 말로 메서드(Method)라고도 한다. 어려운 용어이지만 반드시 익혀 두도록 하자(즉, setdata라는 함수는 FourCal 클래스의 메서드이다). 그렇다면 이제 setdata 메서드에 대해서 자세하게 알아보자.

일반적인 함수를 만들 때 우리는 다음과 같이 작성한다.

def sum(a, b): return a+b

즉, 입력값이 있고 수행하는 문장이 있다. 메서드도 마찬가지이다.

setdata 메서드를 다시 보면 아래와 같다.

def setdata(self, first, second): # ① 메서드의 입력 인수 self.first = first # ② 메서드의 수행문 self.second = second # ② 메서드의 수행문

① setdata 메서드의 입력 인수

입력 인수로 self, first, second라는 3개의 입력값을 받는다. 그런데 일반적인 함수와는 달리 메서드의 첫 번째 입력 인수는 특별한 의미를 갖는 self라는 변수이다.

다음의 예를 보면서 자세히 살펴보자.

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

위에서 보는 것처럼 a라는 객체를 만든 다음에 a.setdata(4, 2)처럼 하면 FourCal 클래스의 setdata 메서드가 호출되고 setdata 메서드의 첫 번째 인수에는 자동으로 a라는 인스턴스가 입력으로 들어가게 된다.

즉, setdata의 입력 인수는 self, first, second로 총 3개이지만 a.setdata(4, 2)처럼 2개의 입력값만 주어도 a라는 인스턴스가 setdata 함수의 첫 번째 입력을 받는 변수인 self에 대입되게 된다.

self: 객체 a, first: 4, second: 2

파이썬 클래스에서 가장 헷갈리는 부분이 바로 이 부분이다. setdata라는 함수는 입력 인수로 3개를 받는데 왜 a.setdata(4, 2)처럼 2개만 입력해도 실행이 되는가? 이 질문에 대한 답변을 여러분도 이제는 알았을 것이다.

 

 

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

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

FourCal.setdata(a, 4, 2)

위와 같이 "클래스명.메서드" 형태로 호출할 때는 객체 a를 입력 인수로 꼭 넣어 주어야 한다. 반면에 앞에서 보았듯이 "객체.메서드" 형태로 호출할 때는 첫 번째 입력 인수(self)를 반드시 생략해야 한다.

 

 

그 다음으로 중요한 사항을 살펴보자.

② setdata 메서드의 수행문

setdata 함수에는 수행할 문장이 2개 있다.

self.first = first self.second = second

위 수행문이 뜻하는 바는 무엇일까? 입력 인수로 받은 first는 4이고 second는 2라는 것은 앞에서 이미 알았다. 그렇다면 위의 문장은 다음과 같이 바뀔 것이다.

self.first = 4 self.second = 2

여기서 중요한 것은 바로 self이다. self는 a.setdata(4, 2)처럼 호출했을 때 자동으로 첫 번째 입력 인수로 들어오는 인스턴스 a라고 했다. 그렇다면 self.first의 의미는 무엇이겠는가? 당연히 a.first가 될 것이다. 또한 self.second는 당연히 a.second가 될 것이다.

따라서 위의 두 문장을 풀어서 쓰면 다음과 같이 된다.

a.first = 4 a.second = 2

정말 이런지 직접 출력해서 확인해 보자.

>>> a = FourCal() >>> a.setdata(4, 2) >>> print(a.first) 4 >>> print(a.second) 2

b라는 객체를 하나 더 만들어 보자.

>>> b = FourCal() >>> b.setdata(3, 7) >>> print(b.first) 3 >>> print(a.first) 4

a와 b라는 객체는 모두 first라는 변수를 가지고 있지만 그 변수의 값은 각기 다르다. b 객체의 first 변수에 3이라는 값을 대입하더라도 a의 first 값이 3으로 변경되지는 않는다. a, b 객체는 모두 고유한 저장 영역을 가지고 있기 때문이다.

객체의 변수(예: self.first)는 그 객체에서만 사용되는 값이다. 다른 객체들과 별도로 그 값을 유지한다는 점을 꼭 기억하도록 하자. 클래스에서는 이 부분을 이해하는 것이 가장 중요하다.

지금껏 완성된 클래스를 다시 작성해 보면 다음과 같다.

>>> class FourCal: ... def setdata(self, first, second): ... self.first = first ... self.second = second ... >>>

지금까지 살펴본 내용이 바로 위의 4줄을 설명하기 위한 것이었다. 위에서 설명한 것들이 이해가 되지 않는다면 다시 한 번 읽어 보기 바란다. 이 부분을 이해하지 못 하면 다음으로 넘어갈 수 없기 때문이다.

더하기 기능 만들기

자! 그럼 2개의 숫자값을 설정해 주었으니 2개의 숫자를 더하는 기능을 방금 만든 클래스에 추가해 보자. 우리는 다음과 같이 더하기 기능을 갖춘 클래스를 만들어야 한다.

>>> a = FourCal() >>> a.setdata(4, 2) >>> print(a.sum()) 6

이를 가능하게 하기 위해 FourCal 클래스를 다음과 같이 만들었다.

>>> class FourCal: ... def setdata(self, first, second): ... self.first = first ... self.second = second ... def sum(self): ... result = self.first + self.second ... return result ... >>>

새롭게 추가된 것은 sum이라는 메서드이다. 이 sum 메서드만 따로 떼어 내서 생각해 보자.

def sum(self): result = self.first + self.second return result

입력으로 받는 값은 self밖에 없고 돌려주는 값은 result이다. a.sum()처럼 수행하면 sum 함수에 자동으로 객체 a가 첫 번째 입력 인수로 들어가게 된다는 것을 명심하자.

이번에는 돌려주는 값을 보자.

result = self.first + self.second

위의 내용은 아래와 같이 해석할 수 있다.

result = a.first + a.second

위의 내용은 a.setdata(4, 2)에서 a.first = 4, a.second = 2라고 이미 설정되었기 때문에 다시 다음과 같이 해석된다.

result = 4 + 2

따라서 아래와 같이 실행하면 6이 화면에 출력된다.

>>> print(a.sum()) 6

여기까지 모두 이해한 독자라면 클래스에 대해 80% 이상을 안 것이다. 파이썬의 클래스는 그다지 어렵지 않다.

곱하기, 빼기, 나누기 기능 만들기

이번에는 곱하기, 빼기, 나누기 등을 할 수 있게 만들어 보자.

>>> class FourCal: ... def setdata(self, first, second): ... self.first = first ... self.second = second ... def sum(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 ... >>>

mul, sub, div 모두 sum 함수에서 배운 것과 동일한 방법이니 따로 설명하지는 않겠다.

정말로 모든 것이 제대로 동작하는지 확인해 보자.

>>> a = FourCal() >>> b = FourCal() >>> a.setdata(4, 2) >>> b.setdata(3, 7) >>> a.sum() 6 >>> a.mul() 8 >>> a.sub() 2 >>> a.div() 2 >>> b.sum() 10 >>> b.mul() 21 >>> b.sub() -4 >>> b.div() 0

우리가 목표로 했던 사칙연산 기능을 가진 클래스를 만들어 보았다. a 객체와 b 객체는 서로 다른 저장 공간을 가지고 있기 때문에 위에서 봤듯이 완전히 독립적으로 동작하는 것을 알 수 있다. 즉, 클래스에 의해서 생성된 객체들은 다른 객체들과 완전히 다른 저장 공간을 가지고 독립적으로 동작한다는 것을 잊지 말자.

(※ 이러한 의미에서 객체 지향 프로그래밍(Object oriented Programming)이라는 말이 생겨나게 된 것이다.)

"박씨네 집" 클래스 만들기

이번에는 전혀 다른 내용의 클래스를 한번 만들어 보자. 사칙연산 클래스보다 조금 더 재미있는 "박씨네 집"이라는 클래스를 만들어 보겠다. 먼저 이 클래스가 어떤 식으로 동작하게 할지 생각해 보자.

클래스 구상하기

1. 클래스 이름은 HousePark으로 하자. 다음처럼 pey라는 인스턴스를 만든다.

>>> pey = HousePark()

2. pey.lastname을 출력하면 "박씨네 집"이라는 클래스에 걸맞게 "박"이라는 성을 출력하게 만들기로 하자.

>>> print(pey.lastname) 박

3. 이름을 설정하면 pey.fullname이 성을 포함한 값을 가지도록 만든다.

>>> pey.setname("응용") >>> print(pey.fullname) 박응용

4. 여행 가고 싶은 장소를 입력으로 주면 다음과 같이 출력해 주는 travel 함수도 만들어 보자.

>>> pey.travel("부산") 박응용, 부산여행을 가다.

우선 여기까지만 만들어 보겠다. 1번부터 4번까지 순서대로 따라 해보면 어렵지 않을 것이다.

클래스 기능 만들기

1. 먼저 아무런 기능 없이 단순히 객체만 생성할 수 있는 클래스는 다음처럼 만들 수 있다.

>>>> class HousePark: ... pass ... >>>

2. 이렇게 클래스를 생성하면 pey = HousePark()처럼 입력해서 객체를 만들 수 있다. 이번에는 pey.lastname을 수행하면 "박"을 출력하기 위해 pass를 삭제한 후 다음처럼 입력해 보자.

>>> class HousePark: ... lastname = "박" ... >>>

lastname은 클래스 변수이다. 이 클래스 변수 lastname은 HousePark 클래스에 의해서 생성되는 인스턴스 모두에 "박"이라는 값을 갖게 만든다.

다음의 예를 보자.

>>> pey = HousePark() >>> pes = HousePark() >>> print(pey.lastname) 박 >>> print(pes.lastname) 박

앞에서 봤듯이 HousePark 클래스에 의해서 생긴 인스턴스 pey와 pes 모두 lastname이 "박"으로 설정되는 것을 확인할 수 있다.

(※ 클래스 변수는 HousePark.lastname처럼 "클래스명.변수명"으로도 사용할 수 있다.)

3. 이제 이름을 설정하고 print(pey.fullname)을 수행하면 성을 포함한 이름을 출력하도록 만들어 보자.

>>> class HousePark: ... lastname = "박" ... def setname(self, name): ... self.fullname = self.lastname + name ... >>>

우선 이름을 설정하기 위해 setname이라는 메서드를 만들었다. 이 메서드는 다음처럼 사용할 수 있다.

>>> pey = HousePark() >>> pey.setname("응용")

setname 함수에 "응용"이라는 값을 인수로 주면 self.fullname에는 결국 "박" + "응용"이라는 값이 대입된다.

"응용"을 입력으로 주었을 때 "박응용"이 생성되는 과정을 살펴보자. 다음은 성을 포함한 이름을 만드는 문장이다.

self.fullname = self.lastname + name

setname 메서드에서 두 번째 입력값 name은 "응용"이므로 위의 문장은 다음과 같이 바뀐다.

self.fullname = self.lastname + "응용"

이 문장에서 self는 setname 함수의 첫 번째 입력으로 들어오는 pey라는 인스턴스이기 때문에 다시 다음과 같이 바뀐다.

pey.fullname = pey.lastname + "응용"

pey.lastname은 클래스 변수로 항상 "박"이라는 값을 갖기 때문에 다음과 같이 바뀐다.

pey.fullname = "박" + "응용"

따라서 pey.fullname을 출력하면 다음과 같은 결과를 얻을 수 있다.

>>> print(pey.fullname) 박응용

4. 이제 우리가 만들려고 했던 클래스의 기능 중 단 한 가지만 남았다. 입력받은 장소로 박응용이 여행을 간다고 출력해 주는 travel 메서드를 HousePark 클래스에 구현해 보자.

>>> class HousePark: ... lastname = "박" ... def setname(self, name): ... self.fullname = self.lastname + name ... def travel(self, where): ... print("%s, %s여행을 가다." % (self.fullname, where)) ... >>>

travel 메서드는 입력값으로 인스턴스(self)와 장소(where)를 받는다. 그리고 해당 값들을 문자열 포매팅 연산자(%s)를 이용하여 문자열에 삽입한 후 출력한다.

위 클래스는 다음과 같이 사용할 수 있다.

>>> pey = HousePark() >>> pey.setname("응용") >>> pey.travel("부산") 박응용, 부산여행을 가다.

위의 과정을 travel 함수의 입장에서 살펴보면 다음과 같다.

우선 travel 함수의 입력 변수인 self와 where은 다음과 같을 것이다.

self: pey where : "부산"

따라서 self.fullname은 pey.fullname이 될 것이고, pey.fullname은 pey.setname("응용")에 의해서 만들어진 "박응용"이 될 것이다. 따라서 pey.travel("부산")처럼 입력하면 위의 예처럼 출력되는 것이다.

초깃값 설정하기

우리는 위에서 HousePark이라는 클래스를 이용해서 인스턴스를 만들었다. 그리고 이 인스턴스에 setname 함수를 이용해서 사람 이름을 설정해 주는 방식을 사용했다.

그런데 위에서 만든 함수를 다음과 같이 실행하면 어떻게 될까?

>>> pey = HousePark() >>> pey.travel("부산")

다음과 같은 오류가 발생한다.

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in travel AttributeError: 'HousePark' object has no attribute 'fullname'

오류가 발생한 이유는 travel 함수가 self.fullname이라는 변수를 필요로 한다는 데서 찾을수 있다. 앞에서 말했듯이 이름을 설정하는 self.fullname 변수는 setname이라는 함수에 의해서 생성된다. 그런데 위의 경우 setname 함수가 실행되지 않아 오류가 발생하는 것이다.

클래스를 설계할 때 이렇게 오류가 발생할 수 있는 상황을 차단하지 못 하면 좋은 클래스라고보기 어렵다. 따라서 이런 오류가 발생하는 것을 방지하기 위해 pey라는 객체를 만드는 순간 setname 메서드가 동작하게 한다면 상당히 편리할 것이다. 이러한 생각으로 나오게 된 것이 바로 __init__ 이라는 메서드이다.

__init__ 메서드로 초깃값을 설정한다

자, 그렇다면 HousePark 클래스를 다음과 같이 바꾸어 보자.

>>> class HousePark: ... lastname = "박" ... def __init__(self, name): ... self.fullname = self.lastname + name ... def travel(self, where): ... print("%s, %s여행을 가다." % (self.fullname, where)) ... >>>

setname 메서드 이름이 __init__으로 바뀌기만 했다.

이것이 어떤 차이를 가져오는지 알아보자. 다음처럼 입력 해 보자.

>>> pey = HousePark() TypeError: __init__() takes exactly 2 arguments (1 given)

위와 같은 오류 메시지를 볼 수 있을 것이다. 메시지를 읽어 보면 알 수 있듯이 pey = HousePark()이라고 입력하는 순간 __init__ 메서드가 호출된다. __init__ 메서드는 2개의 입력값(self, name)이 필요한데 1개의 입력값만 받았기 때문에 오류가 발생한 것이다. 여기서 __init__ 메서드가 받은 입력값 1개는 입력 인수 self를 통해 받는 pey라는 객체이다.

그렇다면 객체를 만들 때 어떻게 __init__ 메서드에 입력값을 2개 줄 수 있을까? 그 방법은 다음과 같다.

>>> pey = HousePark("응용")

이전에 보았던 setname 메서드를 사용하는 방법인 pey.setname("응용")과 아주 비슷하다. 다만 객체를 생성하는 순간에 입력값으로 "응용"이라는 값을 주는 점만 다르다.

이렇게 __init__ 메서드를 이용하면 인스턴스를 만드는 동시에 초깃값을 줄 수 있기 때문에 훨씬 편리하다.

(※ __init__ 메서드는 생성자(Constructor)라고도 한다.)

이제 다음과 같이 입력해 보자. 오류 없이 잘 실행되는 것을 확인할 수 있을 것이다.

>>> pey = HousePark("응용") >>> pey.travel("태국") 박응용, 태국여행을 가다. 클래스의 상속

상속(Inheritance)이란 "물려받다"라는 뜻으로, "재산을 상속받다"라고 할 때의 상속과 같은 의미이다. 클래스에도 이런 개념을 적용할 수가 있다. 어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만드는 것이다. 우리는 "박씨네 집"이라는 HousePark 클래스를 만들었다. 이번엔 "김씨네 집"이라는 HouseKim 클래스를 만들어 보자.

상속의 개념을 이용하면 다음과 같이 간단하게 HouseKim 클래스를 만들 수 있다. 다음은 HouseKim이라는 클래스가 HousePark 클래스를 상속받는 예제이다. HousePark 클래스는 이미 만들어 놓았다고 가정한다.

>>> class HouseKim(HousePark): ... lastname = "김" ... >>>

성(lastname)을 "김"으로 고쳤을 뿐 그 외에는 아무것도 추가하지 않았다.

앞에서 봤듯이 클래스명 뒤 괄호 안에 다른 클래스명을 넣어 주면 상속을 받게 된다.

class 상속받을 클래스명(상속할 클래스명)

이제 다음과 같이 따라 해보자.

>>> juliet = HouseKim("줄리엣") >>> juliet.travel("독도") 김줄리엣, 독도여행을 가다.

HouseKim 클래스는 HousePark 클래스의 모든 기능을 그대로 상속받았기 때문에 __init__ 메서드와 travel 메서드를 구현하지 않더라도 HousePark 클래스와 완전히 동일하게 동작하는 것을 볼 수 있다.

재미있지 않은가? 상속의 개념을 이용하면 독자는 "양씨네 집", "이씨네 집" 등을 쉽게 만들 수있을 것이다.

메서드 오버라이딩

상속의 개념 중 한 가지 더 알아야 할 것이 있다. 클래스를 만들다 보면 상속받을 대상인 클래스의 메서드와 이름은 같지만 그 행동을 다르게 해야 할 때가 있다. 이럴 때는 어떻게 해야 할까?

이전 예제에서 "김씨네 집" 클래스는 "박씨네 집" 클래스를 상속받았다. 우리는 여기서 "김씨네 집" 클래스가 상속받은 travel 함수를 "박씨네 집" 클래스의 travel 함수와 다르게 동작하도록 만들어 볼 것이다.

>>> juliet = HouseKim("줄리엣") >>> juliet.travel("독도", 3) 김줄리엣, 독도여행 3일 가네.

(※ HouseKim 클래스의 travel 메서드는 여행 갈 장소와 여행 기간을 모두 출력한다.)

앞의 예처럼 행동할 수 있도록 HouseKim 클래스를 만들어 보자.

>>> class HouseKim(HousePark): ... lastname = "김" ... def travel(self, where, day): ... print("%s, %s여행 %d일 가네." % (self.fullname, where, day)) ... >>>

travel 함수를 다르게 설정하고 싶으면 동일한 이름의 travel 함수를 HouseKim 클래스 내에서 다시 구현하면 된다. 정말 간단하지 않은가! 이렇게 메서드 이름을 동일하게 다시 구현하는 것을 메서드 오버라이딩(Overriding)이라고 한다.

연산자 오버로딩

연산자 오버로딩(Overloading)이란 연산자(+, -, *, /,,, )를 객체끼리 사용할 수 있게 하는 기법으로, 연산자 오버로딩을 사용하면 다음과 같이 동작하도록 만들 수 있다.

>>> pey = HousePark("응용") >>> juliet = HouseKim("줄리엣") >>> pey + juliet 박응용, 김줄리엣 결혼했네 >>>

이전에 만들어 본 클래스들에 몇 가지 사항을 추가하여 다음처럼 작성해 보자.

# house.py class HousePark: lastname = "박" def __init__(self, name): self.fullname = self.lastname + name def travel(self, where): print("%s, %s여행을 가다." % (self.fullname, where)) def love(self, other): print("%s, %s 사랑에 빠졌네" % (self.fullname, other.fullname)) def __add__(self, other): print("%s, %s 결혼했네" % (self.fullname, other.fullname)) class HouseKim(HousePark): lastname = "김" def travel(self, where, day): print("%s, %s여행 %d일 가네." % (self.fullname, where, day)) pey = HousePark("응용") juliet = HouseKim("줄리엣") pey.love(juliet) pey + juliet

위의 프로그램을 실행시키면 다음과 같은 결과를 얻을 수 있다.

박응용, 김줄리엣 사랑에 빠졌네 박응용, 김줄리엣 결혼했네

위 프로그램의 실행 부분인 다음을 살펴보자.

pey = HousePark("응용") juliet = HouseKim("줄리엣") pey.love(juliet) pey + juliet

먼저 pey = HousePark("응용")으로 pey라는 객체를 만들고 juliet이라는 객체도 생성한다. 그런 다음 pey.love(juliet)으로 love 메서드를 호출한다.

love 메서드를 보면 입력 인수로 2개의 객체를 받는 것을 알 수 있다.

def love(self, other): print("%s, %s 사랑에 빠졌네" % (self.fullname, other.fullname))

따라서 pey.love(juliet)과 같이 호출하면 self에는 pey가 들어가고 other에는 juliet이 들어간다. 따라서 "박응용, 김줄리엣 사랑에 빠졌네"라는 문장이 출력된다.

자! 이제 다음 문장을 살펴보자.

pey + juliet

더하기 기호인 +를 이용해서 객체끼리 더하려고 한다. 이렇게 + 연산자를 객체에 사용하게 되면 HousePark 클래스의 __add__ 라는 함수가 자동으로 호출된다.

HousePark 클래스에서 __add__ 함수가 선언된 부분을 보면 다음과 같다.

def __add__(self, other): print("%s, %s 결혼했네" % (self.fullname, other.fullname))

pey + juliet처럼 호출되면 __add__(self, other) 메서드의 self는 pey가 되고 other는 juliet이 된다. 따라서 "박응용, 김줄리엣 결혼했네"라는 문자열이 출력된다.

"박씨네 집" 클래스 완성하기

자, 이젠 지금껏 만들어 본 클래스로 이야기를 만들어 보자. 스토리는 다음과 같다.

박응용은 부산에 놀러 가고 김줄리엣도 우연히 3일 동안 부산에 놀러 간다. 둘은 사랑에 빠져서 결혼하게 된다. 그러다가 바로 싸우고 이혼을 하게 된다.

참으로 슬픈 이야기이지만 연산자 오버로딩을 공부하기에 더없이 좋은 이야기이다.

pey = HousePark("응용") juliet = HouseKim("줄리엣") pey.travel("부산") juliet.travel("부산", 3) pey.love(juliet) pey + juliet pey.fight(juliet) pey - juliet

위와 같이 동작시키면 결과값이 다음처럼 나오게 만드는 클래스를 작성하는 것이 우리의 목표이다.

박응용 부산여행을 가다. 김줄리엣 부산여행 3일 가네. 박응용, 김줄리엣 사랑에 빠졌네 박응용, 김줄리엣 결혼했네 박응용, 김줄리엣 싸우네 박응용, 김줄리엣 이혼했네

앞에서 보면 기존의 클래스에 fight라는 메서드를 추가시켜야 한다. 그리고 pey - juliet을 수행하기 위해 __sub__ 이라는 빼기(-) 연산자가 사용되었을 때 호출되는 메서드를 만들어 주어야 한다. 이것도 역시 연산자 오버로딩을 사용한다.

(※ +, - 이외에 다른 연산자들의 오버로딩도 가능하다.(예: 곱하기(*)는 __mul__, 나누기(/)는 __truediv__등))

자, 최종적으로 만들어진 "박씨네 집" 클래스를 공개한다.

class HousePark: lastname = "박" def __init__(self, name): self.fullname = self.lastname + name def travel(self, where): print("%s, %s여행을 가다." % (self.fullname, where)) def love(self, other): print("%s, %s 사랑에 빠졌네" % (self.fullname, other.fullname)) def fight(self, other): print("%s, %s 싸우네" % (self.fullname, other.fullname)) def __add__(self, other): print("%s, %s 결혼했네" % (self.fullname, other.fullname)) def __sub__(self, other): print("%s, %s 이혼했네" % (self.fullname, other.fullname)) class HouseKim(HousePark): lastname = "김" def travel(self, where, day): print("%s, %s여행 %d일 가네." % (self.fullname, where, day)) pey = HousePark("응용") juliet = HouseKim("줄리엣") pey.travel("부산") juliet.travel("부산", 3) pey.love(juliet) pey + juliet pey.fight(juliet) pey - juliet

결과값은 예상한 대로 다음처럼 나올 것이다.

박응용, 부산여행을 가다. 김줄리엣, 부산여행 3일 가네. 박응용, 김줄리엣 사랑에 빠졌네 박응용, 김줄리엣 결혼했네 박응용, 김줄리엣 싸우네 박응용, 김줄리엣 이혼했네
마지막 편집일시 : 2016년 5월 21일 2:50:01 오후
<ins class="adsbygoogle" data-ad-client="ca-pub-9470517771012578" data-ad-slot="3437110462" data-adsbygoogle-status="done" style="box-sizing: border-box; display: inline-block; width: 336px; height: 280px;"><ins id="aswift_0_anchor" style="box-sizing: border-box; display: block; border: none; height: 280px; margin: 0px; padding: 0px; position: relative; visibility: visible; width: 336px;"><iframe width="336" height="280" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_0" name="aswift_0" style="box-sizing: border-box; left: 0px; top: 0px;"></iframe></ins></ins>

댓글 88 피드백

덕분에 열공 중입니다. 지금 어렴풋이나마 class에 접근중입니다만 class 내에서의 변수들을 어떻게 다뤄야 하는지도 설명해주시면 고맙겠습니다. - 으악새, 2009년 6월 8일 2:21:00 오후
네, 클래스 변수들에 대한 설명을 좀 더 보강하도록 하겠습니다. 감사합니다. - 박응용, 2009년 6월 8일 5:11:00 오후
중간에~~ first name이 성이 아니구, last name이 성이에요~ :) - jwparkinf8, 2009년 9월 26일 2:26:00 오후
잘보고 있습니다 :) 그리고 궁금한 점이 하나 있는데, print pey.fullname 이 부분을 보고 잠시 당황스러웠는데, 함수안에서 선언된 변수를 외부에서 사용 가능한가요? 함수 호출이 종료 됨과 동시에 변수가 소멸하는 것이 아닌가보군요?? 프로그램 끝날때까지 남는 건가요? - jwparkinf8, 2009년 9월 26일 2:39:00 오후
참, 그리고 보니, 객체 소멸 순서가 제게서는 다르게 나오는데, 음.... 파이썬 버전마다 다른건가요? 아니면 다른 이유가 있는 건가요 - jwparkinf8, 2009년 9월 26일 3:27:00 오후
1) 아, 이런 실수를.. first name 부분은 last name으로 곧 수정조치 하겠습니다. 예상외로 수정해야 할 곳이 많군요 ㅜㅜ. 2) pey라는 인스턴스 변수가 함수내에서 생성된 것이 아니라 클래스 외부에서 만들어졌을것 같습니다. 3) 저도 확인은 못해봤는데요, 생성된 순서로 소멸되는 게 아닌가 봅니다. - 박응용, 2009년 9월 28일 9:00:00 오전
self: peywhere : “부산“ 이 부분 수정이 필요할듯 하네요 - doolyes, 2009년 12월 2일 11:06:00 오후
클래스 변수와 함수 변수 혼용시( ex)self.fullname = self.lastname + name) self가 붙고 안붇고의 차이점 설명이 있었으면 좋겠습니다. - doolyes, 2009년 12월 2일 11:11:00 오후
알려주신 부분 수정했습니다. 감사합니다. 클래스 변수와 인스턴스 변수에 대한 설명을 보강하도록 할께요. 좋은의견 감사합니다. ^^ - 박응용, 2009년 12월 3일 9:28:00 오전
>>> class Service: secret = "Yonunggu is foolish" def __init__(sef, name): self.name = name def sum(self, a, b): result = a + b print "%s %s + %s = %s." % (self.name, a, b, result) >>> tey = Service('Hwang') Traceback (most recent call last): File "<pyshell#95>", line 1, in <module> tey = Service('Hwang') File "<pyshell#93>", line 4, in __init__ self.name = name NameError: global name 'self' is not defined 이렇게 에러가 납니다. 왜 그런 걸까요? 궁금합니다.Python 버전은 2.6.4입니다. 그리고 왕 초보입니다. ^^ - 황재호, 2009년 12월 4일 2:36:00 오전
def __init__(sef, name): 이 부분을 def __init__(self, name): 이렇게 수정하시면 될것 같습니다. - 박응용, 2009년 12월 4일 12:42:00 오후
아이공..l자가 빠져 있었군요. ㅡ,.ㅡ;;; 아무리 해도 안되길래... 감사합니다. 덕분에 잘 공부 하고 있습니다. 감사합니다. - 황재호, 2009년 12월 4일 1:22:00 오후
객체지향프로그래밍(OOP)를 이렇게 쉽게 설명하신분은 처음입니다. ㄱ........... 열공하고 있습니다 ㅋ - codefly, 2009년 12월 6일 5:32:00 오후
감사합니다. ^^ - 박응용, 2009년 12월 7일 9:34:00 오전
큰 따옴표부분이 pey.setname(“박응용”) 나 julliet = HouseKim("줄리엣“) 처럼 “ ” " 를 혼용해서 쓰이고 있는데 모두 가능한 건가요? - 현수, 2009년 12월 26일 12:56:00 오후
편집과정에서 문자열이 좀 변경된것 같습니다. 일반적인 쌍따옴표(")를 사용하시면 됩니다. 좋은의견 감사합니다. ^^ - 박응용, 2009년 12월 28일 9:43:00 오전
파이썬,아니 프로그래밍 초짜입니다. 딱딱한 내용을 쉽고 재미있게 풀어주셔서 너무 감사합니다. 반복해서 읽어보겠습니다. - 미국에서, 2010년 10월 27일 2:07:00 오후
문서가 너무길어서 (모바일앱) 단락 바로가기좀 만들어주세요 감사 - plumpgentoo, 2011년 2월 17일 12:19:41 오후
다음버전에서 검토 해 보도록 하겠습니다. 좋은 의견 감사합니다. - 박응용, 2011년 2월 17일 1:29:28 오후
GAE, Django에 실려 파이썬이 좀 더 널리 보급되었으면 좋겠습니다. 잘 배우고 있습니다. 감사합니다 응용님! 자주 오겠습니다~ - 장군, 2011년 4월 6일 4:53:28 오후
역시 처럼 변수 a가 인스턴스임을 보여준다. // 오타가 난 것 같은데, 제가 바로잡지는 못하겠어서 댓글만 달아놓습니다. - son2son, 2011년 4월 12일 2:21:33 오후
알려주셔서 감사합니다. 수정했습니다. - 박응용, 2011년 4월 13일 12:46:34 오전
저 왜 도스에서 실행시키면 한글은 asci code 불라불라 하면서 안뜨는데 영어로 하니까 뜨더라고요 왜 윈도우의 도스에서 실행이 안되는지알려주세요 그리고 한글도 인식하는 방법이 따로있으면 좀 알려주세요 - 이훈상, 2011년 5월 23일 9:13:16 오전
갤럭시로보고있는데 넘넘좋군요^^! - lmi, 2011년 7월 6일 12:08:58 오전
안녕하세요 ^^ 클래스부분 공부를 하려고 참고하고 있는데 좋은내용 쉽게써주셔서 감사합니다! 그런데 질문이 하나 있어서 글 남깁니다. __del__(self) 부분인데요 파이썬 2.7.2 버전을 사용중인데 이 부분이 정상적으로 출력이 안되는데 버전 문제일까요?ㅎㅎ - cdpython, 2011년 12월 19일 9:43:02 오후
파이썬 클래스 기초를 배우고자 읽어봤는데 정말 쉽게 풀이해주셔서 감사합니다. 질문이 한가지 있는데 파이썬 2.7.2 버전을 사용중인데 __del__ 구문 쪽이 실행이 안되네요 ㅎㅎ 문법이 바뀐건지 궁금하네요 ^^; - cdpython, 2011년 12월 20일 7:30:03 오후
멋져요! 오브젝티브 C의 개념과 거의 비슷하군요! 인스턴스라던지 init , 변수 부분 모두 명확하게 도움이 되었습니다. 다만 오버라이드 부분에 대해서는 오브젝티브 c에서 느낀 부분과는 좀 다른 것 같은 기분이 들기도 하구요. 혹시 오브젝티브 c에서 오버라이드 개념을 알려주실 분 계세요? - kinjoga, 2012년 2월 17일 4:57:36 오후
좋은 내용 보면서 잘 배우고 있습니다. 감사합니다. :) - blueasa, 2012년 5월 7일 1:57:06 오전
만약 클래스안에 sum이라는 함수가 있다고 했을 때요 def sum(self,a,b): return a+b 이런식으로 쓰잖아요? 그런데 A와 B라는 각각의 인스턴스에서 A.sum(1,2), B.sum(1,2)라는 두 명령어는 완전히 같은 기능을 하지 않습니까? 그런데 궂이 왜 self라는 구분을 두는지 모르겠습니다. 같은 클래스를 통해서 만들어진 인스턴스면 변수의 값을 다를지 몰라도 함수의 기능은 완전히 같을 텐데요. 여기 나오지 않은 클래스의 다른기능이 있는건가요? - LEE JaeJun, 2012년 7월 10일 2:08:58 오전
파이썬 클래스를 알고 보았는데, 너무나 설명을 잘 해주셔서 다시 한번 개념을 정확히 잡을 수 있어서 좋았습니다. 좋은 문서 감사합니다. - GilbertJun, 2012년 8월 27일 6:06:08 오후
>>> class housejeong(): lastname='Jeong' def __init__(self, name): self.name=name def fullname(self): self.fullname=self.lastname+self.name return fullname def travel(self, place): self.place=place print self.fullname+'%s 여행을 가다.'% place def __del__(self): print '%s 죽네'%self.fullname 이건 잘 되는데 >>> del a <bound method housejeong.fullname of <__main__.housejeong instance at 0x01FA10A8>> 죽네 왜 이렇게 나오는거죠? - Jeong Seong Woon, 2012년 12월 4일 4:35:26 오전
예제를 따라해봤으면 좋겠는데, 진행할 수가 없습니다. 버전 때문인가요. 제가 잘못해서 그런가요. python 3인데, 2.x로 배우기도 그렇고... 끙~! >>> a = FourCal() >>> type(a) <class '__main__.FourCal'> >>> a.setdata(4,2) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'FourCal' object has no attribute 'setdata' >>> a.setdata(4, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'FourCal' object has no attribute 'setdata' >>> - 별모모, 2013년 5월 16일 4:25:17 오후
"박씨네 집” 클래스가 너무 슬퍼요. ㅠㅠ; 깨닫음을 얻고, 다시 만나 행복하게 잘 살았다. 해피엔딩으로 해주세요. 히~! - 별모모, 2013년 5월 16일 4:46:57 오후
[해피엔딩~!] 음~ 하 하! 하! 코드보다 에러메시지가 더 많았던 듯... (python3.x 코드로 작성하면, python 2.x에서 잘 돕니다.) star@momo:~$ python3 house.py 박응용, 부산여행을 가다. 김쥴리엣, 부산여행 3일 가네. 박응용, 김쥴리엣 사랑에 빠졌네 박응용, 김쥴리엣 결혼했네 박응용, 김쥴리엣 싸우네 박응용, 김쥴리엣 성공했네 김쥴리엣 사라졌네 박응용 사라졌네 star@momo:~$ python house.py 박응용, 부산여행을 가다. 김쥴리엣, 부산여행 3일 가네. 박응용, 김쥴리엣 사랑에 빠졌네 박응용, 김쥴리엣 결혼했네 박응용, 김쥴리엣 싸우네 박응용, 김쥴리엣 성공했네 김쥴리엣 사라졌네 박응용 사라졌네 star@momo:~$ ====================================== # house.py # _*_ coding:utf-8 _*_ class HousePark: lastname = "박" def __init__(self, name): self.fullname = self.lastname + name def travel(self, where): print ("%s, %s여행을 가다." % (self.fullname, where)) def love(self, other): print ("%s, %s 사랑에 빠졌네" % (self.fullname, other.fullname)) def fight(self, other): print ("%s, %s 싸우네" % (self.fullname, other.fullname)) def __add__(self, other): print ("%s, %s 결혼했네" % (self.fullname, other.fullname)) def __sub__(self, other): print ("%s, %s 성공했네" % (self.fullname, other.fullname)) def __del__(self): print ("%s 사라졌네" % (self.fullname)) class HouseKim(HousePark): lastname = "김" def travel(self, where, day): print ("%s, %s여행 %d일 가네." % (self.fullname, where, day)) pey = HousePark("응용") julliet = HouseKim("쥴리엣") pey.travel("부산") julliet.travel("부산", 3) pey.love(julliet) pey + julliet pey.fight(julliet) pey - julliet ============================== - 별모모, 2013년 5월 16일 11:40:13 오후
[고맙습니다!] "인스턴스 중심의 프로그래밍~!", 객체 지향 프로그래밍에 대한 감이 팍~! 옵니다. [self/__init__/__del__/상속과 오버로딩]을 이해할 수 있었습니다. self에 대한 개념을 제대로 이해 못할까봐 조마조마 했습니다. 특히, 클래스 제작 방법을 단계별로 이끌어 주셔서 이해하는데 큰 도움이 되었습니다. 고맙습니다. [오탈자 수정] 맞춤법. "두개"는 "두 개"로 띄어 씁니다. "영구는 배꼽이 두개다." => "영구는 배꼽이 두 개다." - 별모모, 2013년 6월 19일 12:25:53 오후
대단히 친절한 설명 감사합니다.교육경험이 많아서 그러신지 처음 배우는 사람들이 어떤부분에서 헷갈려하시는지 잘 알고 계신듯해요. 프로그래머들의 유머감각 또한 빠질수 없죠. - wangchobo, 2013년 7월 2일 3:33:32 오후
To 별모모님. 수정했습니다. 알려주셔서 감사합니다. - 박응용, 2013년 7월 9일 2:02:43 오후
완전 고맙습니다. 매번 컴 언어를 시도하다 좌절을 맛 보았는데요 . 지금 까지는 성공입니다. 감사드립니다. - Kyungrok, 2013년 11월 29일 7:43:13 오후
>>> class FourCal: def setdata(self,first,second): a = first b = set def sum(self): result = a + b return result >>> f=FourCal() >>> f.setdata(4,2) >>> f.sum() Traceback (most recent call last): File "<pyshell#95>", line 1, in <module> f.sum() File "<pyshell#92>", line 6, in sum result = a + b NameError: global name 'a' is not defined >>> class FourCal: def setdata(self,first,second): self.first = first self.second = second def sum(self): result = self.first + self.second return result >>> f=FourCal() >>> f.setdata(4,2) >>> f.sum() 6 >>> class FourCal: def setdata(self,a,b): self.a = a self.b = b def sum(self): result = self.a + self.b return result >>> f=FourCal() >>> f.setdata(4,2) >>> f.sum() 6 제가 연습해보다가 그냥 self를 안붙이고 해봤는데 오류가 뜨더라고요. self가 setdata밑에 설정에 있고 없고에 따라 달라지는 이유가 뭔가요? - Minsu, 2014년 1월 6일 4:37:36 오후
@Minsu님 이 챕터가 self를 설명하기 위한 글이니 다시 한번 읽어보시고, 혹시라도 이해가 안되는 부분이 있으면 다시 알려주시기 바랍니다. - 박응용, 2014년 1월 6일 5:46:14 오후
약간의 혼동은 있지만 또 하다보면 이해할꺼 같습니다. 컴퓨터언어에 대해 문외한이나 다름 없는데 여기서 공부하면서 컴퓨터 언어의 재미를 배우네요 ㅎㅎ 많은 도움이 되고 있습니다^^ - Minsu, 2014년 1월 7일 9:11:39 오전
other 라는 것이 임의로 정한것이 아닌 다른 객체를 입력하는 정해진 인수(?)인가요? - 윌;;, 2014년 1월 16일 2:00:08 오후
그리고 예제 그대로 저장해서 import 햇더니 에러가 떳습니다. 왜 이런건가요? >>> import house >>> f = house.HousePark() Exception AttributeError: "HousePark instance has no attribute 'fullname'" in <bound method HousePark.__del__ of <house.HousePark instance at 0x7f8854113bd8>> ignored Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __init__() takes exactly 2 arguments (1 given) - 윌;;, 2014년 1월 16일 2:13:36 오후
윌;; 님, other 는 임의로 정한 변수명이구요, 다른 객체를 지칭하고 있는게 맞습니다. import 시 오류가 난 이유는 HousePark() 이 아니라 HousePark("응용") 이런식으로 초기값을 주어야 합니다. - 박응용, 2014년 1월 16일 2:16:45 오후
아;; 감사합니다 ^^ - 윌;;, 2014년 1월 16일 2:42:07 오후
class Person(object): def __init__(self, name, age, weight, height): self.name = name self.age = age self.weight = weight self.height = height def bmi(self): return self.weight / (self.height**2) def status(self): if self.bmi < 18.5: return "Underweight" elif self.bmi >= 18.5 and self.bmi < 25: return "Normal" elif self.bmi >= 25 and self.bmi < 30: return "Overweight" else: return "Obese" if statement 부분에서 숫자 비교할때 TypeError: unorderable types: method() < float() me.bmi 라고 에러가 뜨는데 어떤식으로 코딩을 해야돼나요?  - Jinyoung, 2014년 6월 16일 2:02:13 오전
Jinyoung 님, if self.bmi < 18.5 대신 if self.bmi() < 18.5 라고 하셔야 합니다. - 박응용, 2014년 6월 16일 1:05:43 오후
def __init__(self, model, year, speed): 라는 function 에서 speed라는 argument가 주어지지 않을경우에 0으로 간주해서 실행하려면 어덯게 해야돼나요? - Jinyoung, 2014년 6월 16일 4:23:43 오후
Jinyoung 님, def __init__(self, model, year, speed=0): 요런식으로 speed 항목에 초기값을 주시면 되겠네요. - 박응용, 2014년 6월 16일 5:51:58 오후
class Account(object): def __init__(self, account_id, balance, annual_interest_rate): self.account_id = account_id self.balance = balance self.annual_interest_rate = annual_interest_rate def monthly_interest(self): month_rate = self.annual_interest_rate / 12 month_interest = self.balance * (month_rate /100) return month_interest def deposit(self, value): self.balance += value return self.balance def withdraw(self, value): if self.balance < value: return print("There are insufficient funds in your account to make this withdrawal.") else: self.balance -= value return self.balance def __str__(self): return "Account ID: {} \nBalance: ${} \nMonthly interest: ${}".format(self.account_id, self.balance, self.monthly_interest()) my_account = Account(19174, 120, 4.3) my_account.deposit(1000) my_account.withdraw(100) print(my_account) 마지막 __str__ 부분에서 Balance 와 Monthly interest 값을 두번째 소수점 자리로 바꿔서 나타내면 어떻게 해야돼나요? - Jinyoung, 2014년 6월 18일 10:15:42 오전
class ex : a = [] b = [] c = [] ex1 = ex() ex2 = ex() ex3 = ex() 이런 클래스가 있다고 가정했을때, ex1.a.append('kim')를 대입 했습니다. 그런데 ex2.a의 값을 확인해보니 역시 'kim'이 나옵니다. 제가 생각하기엔 ex1과 ex2는 다른 객체이기때문에 서로 독립적이 되어야 하지않나요? 왜 ex2.a가 ex1.a.append의 영향을 받는지 모르겠습니다.. 해결방법이 있을까요?? - ko, 2014년 7월 29일 11:34:52 오전
Jinyoung님 구글에서 찾아보시면 나올텐데 round(arg,자리수) 사용하시면 되세요 - ko, 2014년 7월 29일 11:41:53 오전
안녕하세요 잘보고 있습니다 한가지 의문이 있는데요.,, 클래스를 선언하고 매서드 함수로 인자값을 넘겨줬을때, 왜 함수내에서 self.name = name 같은 동작을 하는건가요? self.name은 하나의 매서드함수 내에서만 사용되는 변수 같은데..왜 그냥 직접 인자값을 넘겨받은 name변수를 직접 사용하지 않는건지 궁금합니다 - 민욱, 2014년 8월 10일 11:33:17 오후
아 한가지 개념적으로 더 궁금한게 있습니다 다른책에서 보면 클래스내의 선언된 변수는 메인문(외부)에서도 사용이 되더라구요 예)myball = ball() <--인스턴스 선언 maball.color = "red" <---매소드에서 사용되는 변수를 메인문에서 문자열을 넣는 모습 myball.size = "small" 제가 좀 헷갈리는게..함수에서는 사용된 변수는 지역변수라 외부에서는 인식이 안되고 그안에서 소멸되잔아요 그런데 클래스이긴하지만..그안에서 만들어진 매소드는 이런 제약이 없는건가요? 아무래도 둘간의 문법이 비슷하다보니 둘간의 차이가 좀 헷갈리는거 같습니다; - 민욱, 2014년 8월 11일 4:01:53 오후
@민욱님, self.name 은 인스턴스 변수입니다. 함수내에서만 사용되는것이 아니라 인스턴스가 만들어지고 값이 세팅되면 해당 인스턴스는 그 값을 계속 가지고 있게됩니다. - 박응용, 2014년 8월 11일 5:22:10 오후
파이썬 버전 3.4.1을 사용하고 있습니다. 다음 예시를 실행하면, 출력이 다르게 나타납니다. >>> class Service: secret = "영구는 배꼽이 두 개다." def __init__(self, name): self.name = name def sum(self, a, b): result = a + b print("%s님, %s + %s = %s입니다." % (self.name, a, b, result)) >>> a = Service("Godard") >>> a.sum(3, 4) Godard님, 3 + 4 = 7입니다. >>> type(a) <class '__main__.Service'> # 이 부분이 예제에서는 <type 'instance'>로 되어 있지만, 클래스에 따라 생성된 인스턴트의 메인 함수(?)쪽을 보여주는 것같습니다. - Joshua, 2014년 8월 12일 5:47:13 오후
def __add__(self, other) 이부분에서 궁금한게 생겼는데요.def __add__(self, other1, other2) 이런식으로 사용할순 없나요?? pey+julliet+park이런식으로 사용할때도 있을것 같아서요...너무 엉뚱한 질문이려나 ^^;; - 공병수, 2014년 9월 4일 11:46:26 오후
@공병수님, 재밌는 생각이네요, + 더하기가 이항연산자라서 아마도 안될것 같습니다. 다만 pey+julliet 이 현재처럼 문자열을 출력하지 않고 HousePark 객체를 리턴하면 (pey + julliet = baby 처럼) pey + juliet + park 으로 계속해서 더하기를 사용할 수 있을것 같네요. - 박응용, 2014년 9월 5일 9:49:09 오전
자바 연산자하고 많이 달라서 이해하기가 조금 어려운면도 있네여... def 로만 이용을 하다가 class는 무슨용도로 이용될까 하면서 배워보는데... - 류성진, 2014년 9월 10일 6:58:35 오후
저도 위에 민욱님과 마찬가지로 self.name을 어떻게 이해해야 할지 개념이 잘 안잡히네요. result 는 클래스변수가 아닌 result가 정의된 클래스함수 내부에서만 유효한 로컬변수 개념이죠. secret은 모든 클래스 함수들에게 모두 유효한 전역변수(즉, 클래스변수) 개념이고요 그렇다면, self.name 은 secret과 완전히 동일한 클래스 변수로 이해해도 되는 건가요? c++ 에서 클래스 변수는 secret처럼 클래스 함수 밖에서 선언을 해주어야만 했는데, 파이썬에서는 클래스변수를 클래스함수 내부에서도 선언을 해줄수 있다는 의미가 됩니까? 위 예제에서는 secret을 클래스 함수들 밖에서 선언해주었는데, 만일 클래스함수 내부에서 선언해 줄려면 앞에 self와 도트연산자를 붙여 self.secret으로 해줘도 무방하다....뭐 이런 정도로 self.name을 이해하면 되는 건가요?  - 이달공, 2014년 9월 12일 2:03:28 오후
@이달공님, 클래스변수(secret)와 인스턴스변수(self.name) 는 차이점이 있습니다. 클래스 변수의 값을 변경하면 해당 클래스로 생성된 객체의 secret 변수값이 모두 함께 변경이 됩니다. 하지만 self.name은 인스턴스 변수이기 때문에 객체별로 설정된 값이 유지됩니다. global과 local의 scope와 비슷한 개념이라고 할 수 있겠네요. c++이나 java를 선행하신분들이 또한가지 헷갈릴 수 있는 부분이 있습니다. 자바 같은 경우 파이썬 클래스 변수처럼 사용하려면 클래스 내부 변수에 public static 키워드를 주어야 하죠. 파이썬에서는 class 블럭에 정의된 변수(메쏘드 블록이 아닌)는 모두 클래스 변수입니다. 인스턴스 변수는 메쏘드 안에서만 선언이 가능하고 무조건 self를 붙여주어야 하구요. 좀 더 명확한 설명이 필요할 듯 해 보이네요. 본문도 좀 더 고민하여 보다 쉽게 파이썬 클래스를 이해할 수 있도록 수정해야 겠습니다. ^^ - 박응용, 2014년 9월 12일 2:21:08 오후
초보자 질문드립니다. class FourCal: ... def setdata(self, first, second): ... self.first = first ... self.second = second ... def sum(self): ... result = self.first + self.second ... return result 보통은 method안에 있는 변수는 로컬변수라고해서 다른 method에 영향을 못 미치지 않나요? (global 변수로 정의되어 있지 않는 한). 하지만 위의 코딩에서 보면 self.first가 sum method에 그대로 쓰이는 즉 global 변수로서의 역할을 수행하고 있는데, 이걸 어떻게 이해하여야 할까요? - CHUNG HYUN, 2014년 11월 4일 10:00:36 오전
@CHUNG HYUN 님, self.first 처럼 self가 붙으면 이 변수는 해당 함수에서만 쓰이는 로컬 변수가 아닌 인스턴스 변수가 됩니다. 따라서 클래스의 메쏘드인 sum 메쏘드 등에서 self.first 와 같이 참조가 가능하게 됩니다. 단, 참조하기 전에 먼저 setdata 메쏘드에서와 같이 self.first 값을 할당하는 과정이 선행되어야 하구요. - 박응용, 2014년 11월 4일 10:44:54 오전
빠른 답변 감사드립니다. 또 질문이 있 는데요.. def setdata(self, first, second): ... self.first = first ... self.second = second 여기서 보면 self.first = first로 정의되어 있는데, 이 .first가 setdata 메소드의 파라메터와 관련이 있나요? .first 대신 뭐 self.fr = first 이렇게 정의를 내려도 상관없는 건가요? 요지는 반드시 self.first = first라고 정의를 해야하는지, self.fr = first 이렇게 해도 상관없는 것인지 묻고 싶습니다. 참 도움이 많이 되는 것 같아 감사드립니다. - CHUNG HYUN, 2014년 11월 4일 12:41:07 오후
@CHUNG HYUN 님, self.fr = first 라고 해도 상관없습니다. 다만 이렇게 바꾸시면 나중에 이 인스턴스 변수를 참조할 때(sum 메쏘드와 같이) self.first가 아닌 self.fr로 사용하면 됩니다. setdata메쏘드의 입력변수인 first는 로컬변수로 메쏘드 내에서만 사용될 뿐입니다. - 박응용, 2014년 11월 4일 1:21:40 오후
class FourCal: def setdata(self, first, second): self.first = first self.second = second def sum(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() a.setdata(4, 2) print a.sum() print a.mul() print a.sub() print a.div() ---------------------------------------- 인터프리터로는 결과를 잘 확인했는데요 노트패트++ 에서 위 코드를 작성해서 cal.py 로 저장한 후 C:\Python27>python cal.py 했는데 그냥 C:\Python27 만 나오네요. - s, 2015년 2월 2일 12:52:54 오후
a = FourCal()..... print a.div() 들을 class 에 줄을 맞추니 되네요.  - s, 2015년 2월 9일 12:33:48 오후
house.py 는 왜 이렇게 깨져서 나올까요? 위에 있는 코드 그대로 카피해서 # coding=utf-8 했는데도...ㅠ.ㅠ 諛뺤쓳?? 遺€???ы뻾??媛€?? 源€以꾨━?? 遺€???ы뻾 3 ??媛€?? 諛뺤쓳?? 源€以꾨━???щ옉??鍮좎죱? 諛뺤쓳?? 源€以꾨━??寃고샎?덈꽕 諛뺤쓳?? 源€以꾨━???몄슦? 諛뺤쓳?? 源€以꾨━???댄샎?덈꽕 諛뺤쓳??二쎈꽕 源€以꾨━??二쎈꽕 - s, 2015년 2월 9일 2:54:37 오후
파이썬 3.4 로 실행하니 잘 돼네요...  - s, 2015년 2월 10일 5:23:19 오후
노트패드++ 에서 인코딩 ANSI로 표시체크하고 # -*- coding : utf-8 -*- 맨 위에 붙여놓기 하고 house.py 치니 2.7에서도 한글로 잘 나오네요 ^^ - s, 2015년 2월 11일 2:54:56 오전
통상적으로 오버라이딩은 메쏘드명 뿐 아니라 인자도 같아야 하는 것으로 알고 있는데, 파이썬에서는 메쏘드명만 같으면 되는건가요? 이걸 어떤 의미로 받아 들여야 할까요? 상속에서의 오버로딩은 오버라이딩으로 인식되고, 상속에서의 오버로딩을 하려면 부모의 메쏘드를 호출하도록 추가로 구현해야 하는것인가요? - Junggu, 2015년 5월 2일 6:22:39 오전
지금 해 보니 파이썬에서의 오버로딩은 안되는 군요.. O_O 좀 놀랐습니다. class A: def add(self, a, b): return a + b def add(self, a, b, c): return a + b + c a = A() a.add(1,2,3) # 6 a.add(1,2) # Error - Junggu, 2015년 5월 2일 6:32:00 오전
조금 더 검색해보니, 오버로딩은 지원하지 않는군요. 음... 출처 : http://coreapython.hosting.paran.com/dive/chap05.html 자바(Java)와 파워빌더(Powerbuilder)는 인자 리스트로 함수 오버로딩을 지원합니다. 다시 말해, 클래스는 이름은 같지만 인자 개수가 다르거나 인자 유형이 다르게 여러 메쏘드를 가질 수 있습니다. 다른 언어들은 (특히 PL/SQL는) 심지어 인자 이름으로 함수 오버로딩도 지원합니다; 즉, 클래스는 이름도 같고 인자 유형도 같지만 인자 이름은 다른 메쏘드를 여럿 가질 수 있습니다. 파이썬은 이 중 어느 것도 지원하지 않습니다; 함수 오버로딩 같은 형태는 전혀 없습니다. 메쏘드는 오직 이름만으로 정의되며, 주어진 이름으로 클래스 당 하나의 클래스만 있을 수 있습니다. 그래서 자손 클래스에 __init__ 메쏘드가 있으면 언제나 조상의 __init__ 메쏘드를 오버라이드 합니다. 자손 클래스에 다른 인자 리스트를 가지고 정의되어 있더라도 말입니다. 그리고 같은 규칙이 다른 메쏘드에도 적용됩니다. 처음 파이썬을 만든 귀도(Guido)는 메쏘드 오버라이딩을 다음과 같이 설명합니다: "파생된 클래스는 바탕 클래스의 메쏘드를 오버라이드할 수 있습니다. 메쏘드는 같은 객체의 다른 메쏘드를 호출할 때 특별한 권한이 없기 때문에, 바탕 클래스의 메쏘드가 같은 바탕 클래스 안에 정의된 또다른 메쏘드를 호출할 경우 실제로 그를 오버라이드한 파생 클래스의 메쏘드를 호출할 가능성이 높습니다. (C++ 프로그래머에게 드리는 주의: 파이썬에서 모든 메쏘드는 효과상 가상적입니다.)" 이해가 되지 않으면 무시해도 좋습니다. (정신이 하나도 없군요) 저는 그냥 무시하고 지나쳐도 된다고 생각했습니다. - Junggu, 2015년 5월 2일 6:33:54 오전
역시 C++보다는 매우 간편하네요. 왜 파이썬이 쉬운지 말해주네요. 그런데 C++ 문법이 익숙한 저로서는, 파이썬 문법이 마치 C++요약집 같은 느낌이 드네요. 어떤 면에서는 C++보다 직관적이지만, C++문법이 더욱 논리적이고 유동적이라 생각이 듭니다. 대신 매우 복잡하죠 C++을 베이스로 프로그래밍할 때,파이썬을 같이 쓴다면, 과연 언제 파이썬으로 만든 클래스를 사용해야 효율적일지 궁금합니다. - kjh, 2015년 6월 13일 6:41:22 오후
시중에서 구입한 책의 클래스에서 계속 막혀서 진도를 못 나갔는데 오늘 여기서 한 번 쭉~ 읽고나니 변비 뚫리듯 시원하게 이해됐어요 감사합니다!! 최고입니다! - 정유찬, 2015년 8월 18일 9:23:39 오후
__init__ 메쏘드, 초기치를 설정한다. 여기 마지막 부분에 print(pey.travel("태국")) <-- 여기 print는 빠져야 겠네요 - Seo-gon, 2015년 11월 1일 5:20:44 오후
@Seo-gon님, 감사합니다. 수정했습니다. - 박응용, 2015년 11월 1일 8:38:23 오후
이제야 객체지향에 대해서 처음 접해보네요... 맨날 C++두꺼운책을 다 보지 못하고 (시간 핑계로) 포기했는데... 정말 감사합니다!!!  - kuck12@nate.com, 2015년 11월 4일 9:53:13 오후
>>> class HousePark: def firstname(self, first): self.firstname = first def lastname(self, last): self.lastname = last def fullname(self): self.fullname = self.firstname + self.lastname print("제 이름은 %s 입니다." % (self.fullname)) >>> pey = HousePark() >>> pey.firstname("최") >>> pey.lastname("정원") >>> pey.fullname() 제 이름은 최정원 입니다. >>> pey.fullname '최정원' >>> 여기서 pey.fullname() vs. pey.fullname 의 결과가 위와 같이 나오네요.... 둘의 정확한 차이는 결과로 보아 이해가되는데 () 의 유무에 따라 차이가 왜 나는지 이해가 되지 않습니다... - 최정원, 2015년 12월 24일 10:47:48 오전
안녕하십니까 이제 갈수록 점점 어려워지는 것 같습니다. 이해되는 시간이 오래 걸리네요ㅠ 연산자 오버로딩 부분에서 아무리 생각해도 아직 이해가 안되네요 pey.love(juliet) --> 왜 self 에는 pey가 other 에는 juliet 가 들어가는지 설명 좀 부탁 드립니다ㅠㅠ - 최정원, 2015년 12월 24일 3:22:46 오후
@최정원 pey.fullname이라고 괄호없이 그냥 입력하면 그것은 그냥 pey라는 객체의 fullname변수를 호출한 것이고 pey.fullname()이라고 괄호를 추가하면 그것은 pey라는 객체의 fullname이라는 메소드, 즉 함수를 호출한 것이기 때문이라고 봅니다 pey.love(juliet)에서 보면 love라는 함수를 호출한 것은 pey객체입니다. 그러면 pey가 본인 즉 self가 되는 것이구요 괄호안에 들어가있는 다른 객체 juliet이 other가 되는 것이라고 볼 수 있겠습니다. - 진우, 2016년 1월 3일 10:52:02 오후
@진우 설명 감사드립니다. 이해하는데 많은 도움이 되었습니다. - 최정원, 2016년 1월 7일 7:47:57 오전
>>> class House: def __init__(self, name): self.name = name def name(self): print("제 이름은 %s 입니다." % self.name) >>> pey = House("정원") >>> pey.name '정원' >>> pey.name() Traceback (most recent call last): File "<pyshell#30>", line 1, in <module> pey.name() TypeError: 'str' object is not callable 위와 같이 코딩하여 실행하여 보니 pey.name 에는 제이름이 변수로 제대로 입력이 된 것 같은데, 메소드를 실행에서 위와 같은 에러 메시지가 뜹니다. 어디서 잘못되었는지 모르겠습니다. - 최정원, 2016년 1월 7일 10:32:10 오전
class _DictWrapper(object): """An object that contains a dictionary.""" def __init__(self, values=None, name=''): self.name = name self.d = {} ----------------------------- 이런 클래스에서요, 클래스 괄호 안에나오는 object는 무슨 의미인가요? 저게 변수도 아니고, 다른 클래스도 아니더라고요. - CharlieBrown, 2016년 3월 29일 1:07:32 오후
안녕하세요. 여기서 파이썬 배우고 있는 초보자입니다. 이해안되는 부분이 있어서 질문 드리는데요. 연산자 오버fh딩의 결혼했네 부분에서 pey.love(juliet) 와 def __add__(self, other): print("%s, %s 결혼했네" % (self.fullname, other.fullname)) 구문이 어떻게 돌아가는 지 잘 모르겠습니다. 말씀처럼 인스턴스는 독립적인 네임스페이스 그 공간에서 데이터와 함수를 갖고 있다고 하셨는데 위 구문에는 매개변수로 인스턴스를 대입하고 있습니다. juliet 인스턴스에 딸린 다른 함수도 있는데 __add__(self, other) 의 other에서 데이터(setname)만 갖고, juliet에 딸린 오버라이딩된 함수(travel)는 갖고 오지 않고 어떻게 되는지요? : - Doh Oon, 2016년 4월 27일 11:27:18 오전
글 무척 잘 봤습니다. 본문에서 "a, b 객체는 모두 고유한 저장 영역(네임스페이스, namespace)을 가지고 있기 때문이다." 라고 했는데, 고유햔 저장 영역 과 네임스페이스는 다르다고 생각되는데요. C++의 네임스페이스와 파이선의 네임스페이스의 개념이 다른걸까요? - Softgear, 2016년 4월 27일 6:23:38 오후
@Softgear님, c++의 네임스페이스와 파이썬의 네임스페이스는 아마도 거의 같은 개념이라고 생각되구요, 말씀하신것처럼 위 문장은 오류가 있네요. 알려주셔서 감사합니다. 조만간 파이썬 네임스페이스에 대한 내용도 추가하도록 하겠습니다. - 박응용, 2016년 4월 27일 10:01:30 오후
응답이 없어서,,저같은 초보들을 위해 제 질문에 제가 답변해 봅니다.^^; juilet 객체가 항상 지니고 다니는 함수와 데이터 중에서 다른 객체의 메쏘드인 __add__ 는 juilet의 데이터 부분(fullname)만 불러서 사용하였다. 결론은,,,객체 덩어리를 타 함수의 매개변수로 넣어도 함수 내부에서는 불러온 객체의 필요한 부분만 선택할수 있다. 이 경우는 fullname만 사용하였다. 객체를 어떤 '값'으로 생각하면 매우 이상하게 보이고 도저히 이해 안된다. 그래서 객체를 값이 아닌 어떤 '작은 프로그램'이라고 생각하여야 하고, 거기서 데이터(값)를 꺼집어내어 사용하거나 거기서 또 메쏘드를 꺼집어 내어 사용할 수 있다....클래스/객체는 모든게 되는 떠다니는 항공모함이다. ㅎㅎ 헤매고 다닌 제 나름대로의 해석입니다만...아닐 수도 있습니다. - Doh Oon, 2016년 4월 29일 11:43:40 오후
one of the fittest
one of the fittest 일상·생각

네 믿은대로 될지어다