안녕하세요. BlockDMask 입니다.
오늘은 클래스, class 라는 것에 대해서 알아보려고 하는데요.
매우 중요한 개념이고, 이걸 어떻게 쓰는가에 따라서 재사용성이 확 늘어나기 때문에 정말 중요한 개념이라고 강조 드리고 싶습니다.
사실 이걸 어떻게 잘 설명해야할지 막막합니다. 최선을 다해서 다양한 예제를 가지고 설명을 해보겠습니다.
그럼에도 불구하고 혹시 제 설명이 부족하다면 다른 분들의 블로그나 도서를 참고하셔서 클래스에 대한 개념을 꼭 익혀서 본인의 것으로 만드셨으면 합니다.
<목차>
1. 클래스?
2. 클래스와 객체
3. 클래스의 생성자와 메서드
4. 클래스 변수와 객체 변수(속성)
5. 클래스 비공개 변수(속성)
클래스 상속, 메서드 오버라이딩, 추상클래스 포스팅 [바로가기]
1. 파이썬 클래스란?
저희는 지난시간에 [함수]라는 것에 대해서 배웠습니다.
지난시간에도 말씀드렸듯이. 함수는 특정 기능을 수행하도록 만들어 놓은 것이라 한다면 클래스는 조금더 큰 개념이라고 할 수 있습니다.
예를들면 스마트폰 이라는 것이 있다고 해봅시다.
스마트폰의 기능은 call() 전화걸기, send_msg() 메시지 보내기, read_msg() 메시지 읽기 등 여러 기능이 있습니다.
여기서 call(), send_msg(), read_msg() 라는 것은 특정한 기능을 하는 것이기 때문에 함수라 할 수 있겠죠?
read_msg(index) 함수 : 해당 index 순서에 맞는 메시지를 읽어주는 함수.
이 read_msg(index) 함수만 가지고 생각을 해보겠습니다. 함수 자체는 문제가 되지 않습니다.
하지만 이 함수를 여럿이서 사용한다고 하면 어떻게 될까요?
A가 read_msg(index)를 통해서 메시지를 읽으려 하고, B도 read_msg(index)를 통해서 메시지를 읽는다고 하면, 어떤게 A의 메시지 인지, B의 메시지 인지 구분하기 어렵지 않을까요?
이것을 코드로 한번 보겠습니다.
1 2 3 4 5 6 7 8 | def read_msg(index): a = ['message1', 'message2', 'message3', 'message4'] return a[index] # p1과 p2의 메시지를 구분할 수 없습니다. p1 = read_msg(3) p2 = read_msg(2) | cs |
이렇게 하나의 함수에서 두개의 메시지를 구분하기란 쉽지않습니다.
"아니 그러면 메시지를 전달해서 주거나, 함수를 두개 만들면 되겠네?" 라는 생각을 할 수 있습니다. 그렇게 한번 해볼까요?
함수를 두개 만들어 볼게요.
1 2 3 4 5 6 7 8 9 10 11 12 13 | def read_msg_p1(index): a = ['엄마다. ..', '밥차려놨다', '공부좀 하렴', '자기야 나야'] return a[index] def read_msg_p2(index): a = ['김미영 팀장입니다.', '선배 조별과제좀!', '연락좀 받으세요'] return a[index] # p1과 p2 전용 함수를 만들어서 메시지를 따로 각각 관리할 수 있습니다. p1 = read_msg_p1(3) p2 = read_msg_p2(2) | cs |
이렇게 전용함수를 만들거나, 아래처럼 하나의 함수에 리스트를 보내도 됩니다.
(물론 이 방법은 보기에 비효율적이지만 일단은 설명을 하기위한 예시이니 너그럽게 넘어가주셨으면 합니다.)
1 2 3 4 5 6 7 8 9 10 11 12 | def read_msg(msgs, index): # ... 메시지 리스트를 받아서 특정 기능을 함 ... return msgs[index] # p1과 p2의 메시지 리스트를 넘겨서 메시지를 읽는다. p1_msg = ['엄마다. ..', '밥차려놨다', '공부좀 하렴', '자기야 나야'] p2_msg = ['김미영 팀장입니다.', '선배 조별과제좀!', '연락좀 받으세요'] p1 = read_msg(p1_msg, 3) p2 = read_msg(p2_msg, 2) | cs |
뭐 이런식으로 리스트를 쫘르르르륵 만들어서 관리해도 됩니다.
그런데 너무 비효율적이지 않나요?
사람이 100명이 되면 100개의 리스트를 따로 관리해야하고, 지금은 메시지 리스트만 있지만 연락처 리스트, 카카오톡 메시지 리스트, 공인인증서 등등 여러 데이터를 관리해야한다고 하면 정말 끔-찍 하게 복잡하지 않을까요?
물론 불가능하지는 않습니다.
아무튼 이렇게 데이터를 조금 효율적으로 관리하기 위해서 클래스라는 개념이 나왔습니다.
이 클래스는 특정기능을 하는 함수들과 변수들을 모아서 하나의 뭉텅이로 관리를해서 마치 새로운 타입처럼 만들어서 데이터를 관리할 수 있는 개념 입니다.
스마트폰 이라고 하면 전화하기, 전화받기, 메시지 보내기, 메시지 받기 라는게있잖아요.
그러면 사람에 따라서 전화하기1, 전화하기2, 전화하기3 을 각각 함수를 만들게 아니라 스마트폰 자체를 스마트폰1, 스마트폰2, 스마트폰3을 각각 만들어 주면 각 스마트폰 마다 전화하기, 전화받기, 메시지 보내기, 메시지 받기가 다 안에 들어가있겠죠??
그 스마트폰을 만들어주는 틀을 클래스라고 합니다. 기계처럼 뚝딱 뚝딱 동일한 기능을 가지고 있는 스마트폰을 만들어주는 틀! "클래스" 입니다.
코드로 한번 비교해볼까요?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #... 사람이 생길때 마다 함수 하나의 쌍 씩. def call_p1(num): # 전화하는 기능 return num def send_msd_p1(): return 'send msg' def read_msd_p1(index): p1_msg = ['김', '메시지2', '메시지3'] return p1_msg[index] def call_p2(num): # 전화하는 기능 return num def send_msd_p2(): return 'send msg' def read_msd_p2(index): p2_msg = ['에이비', '메시지2', '메시지3'] return p2_msg[index] def call_p3(num): # 전화하는 기능 return num def send_msd_p3(): return 'send msg' def read_msd_p3(index): p3_msg = ['이', '메시지111', '메시2'] return p3_msg[index] | cs |
이렇게 각 사람마다 그 기능을 하는 함수를 하나씩 만들어 줘야합니다.
만약에 클래스라는것으로 묶어준다면 아래와 같이 표현할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | # Phone 이라는 클래스를 만들었습니다. class Phone: def __init__(self): self.msg_list = [] def call(self, num): # 전화하는 기능 return num def read_msg(self, index): return self.msg_list[index] def write_msg(self, msg): self.msg_list.append(msg) def send_msg(self, msg): # 누군가에게 메시지를 보냄 pass # Phone 이라는 클래스를 이용해서 각 사람들에게 # 각자의 폰을 줍니다. p1 = Phone() p2 = Phone() p3 = Phone() # p1 메시지 세팅 p1.write_msg('코로나') p1.write_msg('싫어요') # p2 메시지 세팅 p2.write_msg('아빠 힘내세요') p2.write_msg('우리가') p2.write_msg('있잖아요') # p3 메시지 세팅 p3.write_msg('꾸준함이 답이다') p3.write_msg('수험생들 화이팅') p3.write_msg('대학생도 화이팅') | cs |
이런식으로 마치 데이터 타입을 사용하듯이 Phone이라는 클래스를 만들어서 그 기능을 다 묶어서 하나의 틀을 만들고 그 틀을 각각 나눠주는 것 입니다.
결국 데이터를 더 쉽고 체계적으로 관리하기 위해서 클래스라는 개념이 나온 것 입니다.
2. 파이썬 클래스와 객체
완벽하진 않지만 클래스가 어떤 기능을 한것들을 모아둔 "틀"이라고 했습니다.
여기서 한번 더 설명을 해보겠습니다.
편의점에 삼각김밥이라는게 있는데, 다들 아시죠?
(안먹어 봤으면 금수저 이시니까 저에게 도네좀.. 부탁드려요. 농담입니다.)
아무튼 이 삼각김밥이라는게 만들때마다 사람이 손으로 삼각형을 만든는 것이 아니라, 삼각 김밥을 만들수 있는 전용 틀(=클래스)이 있고 그 틀에 참치, 밥, 김, 제육 등등등(=변수, 함수)들을 넣어서 하나의 삼각김밥(=객체)가 나오는것 입니다.
정리하자면 클래스(class)와 객체(object)는 아래와 같습니다.
클래스(class) => 틀
객체(object) 혹은 인스턴스(instance) => 틀에 의해서 실체화된것, 틀에 의해서 만들어짐
**객체와 인스턴스는 거의 비슷한 말로 통하는데요, 정확하게 들어가면 약간 다른 말이기는 합니다. 궁금하다면 구글링 해보세요.!
여기서는 객체와 인스턴스를 동일하게 취급하고 설명하겠습니다.
한번 코드로 다시한번 예시를 들어볼게요. 삼각김밥 클래스 입니다.
제가 봤을때 예제가 아주 이해하기 쉽다고 생각이 들어요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # 삼각김밥 클래스 class Samgak: def __init__(self): self.source = '기본 소스' self.kim = '광O 김' self.bab = '쌀밥' self.food = '' def set_source(self, source_name): self.source = source_name def change_kim(self, kim_name): self.kim = kim_name def change_bab(self, bab_name): self.bab = bab_name def set_food(self, food_name): self.food = food_name def print(self): s1 = 'BlockDMask 가 맛있는 삼각김밥을 만들었습니다.\n' s1 += f'김은 {self.kim} 입니다.\n' s1 += f'밥은 {self.bab} 사용 하였고\n' s1 += f'소스는 {self.source} 촵촵 뿌리고\n' s1 += f'메인은 {self.food}을 넣었습니다.\n' print(s1) # 참치 삼각 김밥 chamchi = Samgak() chamchi.set_food('동O참취') chamchi.set_source('마요네즈') chamchi.print() # 매운 김치 삼각 김밥 kimchi = Samgak() kimchi.set_food('김치') kimchi.set_source('매운소스') kimchi.print() # 멸치 삼각 김밥 mulchi = Samgak() mulchi.change_kim('조O 김') mulchi.set_food('멸치') mulchi.print() | cs |
결과를 보면 똑같은 Samgak 이라는 클래스를 이용해서 세 종류의 삼각김밥 객체를 만든 것을 볼 수 있습니다.
각 객체(삼각김밥)마다 소스, 메인 음식 등을 세팅해서 삼각김밥 고유의 성질(변수, 함수)를 만들어 준 것을 볼 수 있습니다.
이렇게 쉽게 같은것을 만들 수 있는 클래스. 편리하고 매력적이지 않나요?
3. 파이썬 클래스 생성자와 메서드
클래스의 생성자
자 이제 클래스라는것은 대강 알았고 클래스의 고유한 특징에 대해서 알아보려고 합니다.
클래스에서 가장 중요한것은 생성자(constructor)입니다.
이 생성자라는 이름을 생각해보면 클래스가 객체를 "생성"할때 뭔가를 할것 같다는 생각이 들지 않나요?
네, 맞아요 클래스가 객체를 딱 하고생성할때 가장 먼저 들르는 곳이 생성자 입니다.
위 예제에서 우리는 클래스 내부에 있는 __init__ 이라는 것을 보았습니다. 그쵸? 다시한번 볼까요?
1 2 3 4 5 6 7 8 | # 삼각김밥 클래스 class Samgak: def __init__(self): self.source = '기본 소스' self.kim = '광O 김' self.bab = '쌀밥' self.food = '' | cs |
이렇게 def 를 이용한거 보면 함수는 것이겠죠? 이 __init__이라는 특별한 함수를 우리는 클래스에서 생성자 혹은 초기자 라고 부릅니다.
이 생성자는 클래스가 객체를 생성할때 무조건 처음으로 들르는 곳 입니다.
즉. 맨 처음에 자동적으로 __init__ 함수가 불린다는 것 이죠.
그렇기 때문에 우리는 객체를 생성할때, 인스턴스를 생성할때 맨 처음으로 초기화를 해주거나 하는 작업을 이 __init__에서 진행 하면 됩니다.
예제를 보면 우리는 삼각김밥의 기본 소스나, 김, 밥 변수의 초기값을 세팅해주는 작업을 진행했죠? 그렇게 사용하시면 됩니다.
그럼 진짜로 생성자가 자동으로 불리는지 확인한번 해볼까요?
1 2 3 4 5 6 7 8 9 | # 삼각김밥 클래스 class Samgak: def __init__(self): print("생성자 불림") a = Samgak() b = Samgak() | cs |
이렇게 클래스를 정의하고 __init__ 생성자를 만들어서 print를 하게 한번 만들어 보았습니다.
클래스를 이용해서 a 인스턴스와 b 인스턴스를 만들기만 해보았습니다.
그런데 결과값을 보면 "생성자 불림"이 두번이나 출력되는것을 볼 수 있습니다.
이것은 우리는 객체, 인스턴스만 생성했는데, 자동으로 __init__ 함수 (=생성자)가 불리는 것을 알 수 있습니다.
또한 생성자에도 self 이외에 매개변수들을 추가해서 객체를 만들때 인자를 넣어줄 수 있습니다. 예제는 아래 클래스 변수에서 한번 들어보겠습니다.
** 일반적으로 __init__만 알아도 좋지만 __new__라는 것도 있습니다. 더 궁금하신 분들은 구글링 gogo!
클래스의 메서드
메서드는 사실 함수와 다를것이 없습니다. 클래스 내부에 있는 함수를 메서드라고 부를 뿐 입니다. ~~클래스의 ~~메서드라 합니다.
그냥 함수가 클래스에만 들어가면 메서드로 이름세탁을 하는 것이죠.
"자 메서드 설명 끝!" 이라고 하기엔 뭔가 너무 짧으니까 여기서 저기 self 라는 것을 설명해볼까 합니다.
1 2 3 4 5 6 7 8 9 10 11 | # 삼각김밥 클래스 class Samgak: def __init__(self): self.source = '기본 소스' def set_source(self, source_name): self.source = source_name # 참치 삼각 김밥 chamchi = Samgak() chamchi.set_source('마요네즈') | cs |
이 예제를 보면 메서드의 맨 처음 매개변수에 self 라는게 있는걸 볼 수 있습니다.
그리고 변수 앞에 self 라는것도 붙어있죠?
이것은 본인(=객체)를 가리키는 것 입니다.
chamchi.set_souce('마요네즈') 라는것이 실제로 불리면 컴퓨터는 이렇게 호출해줍니다.
set_source(chamchi, '마요네즈') 이렇게 우리가 chamchi라는 객체에 있는 메서드를 호출하게 되면 그 메서드는 본인을 "호출한 객체 자신을 항상 맨 첫번째 매개변수" 로 넘깁니다. 그 객체를 받는것이 바로 self 입니다.
그래서 self를 맨 첫번째 매개변수로 집어 넣는 것 입니다.
self 를 집어넣어서 매개변수로 들어온 self를 이용함으로서 해당 객체의 변수, 메서드에 또 직접 접근할 수 있습니다.
set_source함수의 내부를 보면 self.source 를 이용해서 매개변수로 넘어온 그 객체의 변수를 사용하고 있는 것을 볼 수 있습니다.
4. 파이썬 클래스 변수와 객체 변수(속성)
클래스에서 변수는 클래스 변수와 객체 변수이렇게 두 종류로 나눌 수 있습니다.
아 매개변수로 들어온것은 제외하겠습니다.
사과폰은 해당 회사한테 테클이 들어올 수 있으니 사과회사말고 말고 바나나폰 이라는 클래스를 한번 만들어 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 바나나폰 클래스 class BananaPhone: phone_name = '어른폰' def __init__(self, number, owner): self.number = number self.owner = owner def print(self): s = f'이 폰은 {self.phone_name} 기종이며\n' s += f'번호는 {self.number} 이고\n' s += f'이 폰의 주인은 {self.owner} 입니다.\n' print(s) # 객체, 인스턴스 생성 p1 = BananaPhone('010-1111-2222', '개님') p2 = BananaPhone('010-7777-0000', '고양이님') p1.print() p2.print() | cs |
이렇게 클래스를 만들어 보았습니다.
이 클래스에서 상세히 봐야할것은 세가지가 있습니다.
1) __init__ 생성자에서 self 말고 다른 매개변수를 만들어 주었습니다.
이렇게 객체, 인스턴스를 만듦과 동시에 초기화 해줄것이 있다면 이렇게 __init__에서 매개변수를 추가하고 아래 p1 = BananaPhone('000', '개님') 에서 처럼 생성하면서 인자를 넣어주면 됩니다.
2) 클래스 변수 : 클래스 바로 아래에 있는 phone_name 이라는 변수
클래스 내부에 변수 이름 앞에 self도 붙지 않고, 그 어느 메서드 안에 있지 않은 이 당당한 변수를 우리는 클래스 변수라고 부릅니다.
이 클래스 변수는 마치 폰의 고유한 이름 "i폰", "우주폰"처럼 클래스 고유한 값. 클래스가 객체가 되어도 그 클래스의 근본! 오리지널한 값을 나타낼때 사용하는 변수 입니다.
실제 결과 값을 보아도 모든 객체에서 self.phone_name을 출력해보면 "어른폰"으로 잘 나오는것을 알 수 있습니다. phone_name의 값을 변경해주면 다른 객체에서도 다 변경됩니다. 한번 테스트해보시는것도 좋을것 같네요.
클래스 변수는 최초 선언, 정의할때는 self를 붙이진 않지만, 출력할때 접근할때는 self를 이용해서 접근해줍니다.
3) 객체 변수, 인스턴스 변수 : 메서드 내부에 있고, self가 붙은 변수
이 객체 변수는 각 객체마다 값들이 다를 수 있는 변수들 입니다.
예제를 보면 폰의 주인의 이름과 폰의 번호는 각 폰마다 다를 수 있으므로 이런것들은 객체 변수로 선언해 줍니다.
예제에서는 __init__에서만 선언하고 사용했지만, 다른 메서드에서도 선언 및 사용이 가능합니다.
**클래스의 변수를 클래스의 속성이라 부르기도 합니다.
이렇게해서 클래스에 관해서 간단?히 한번 알아보았는데요.
제 삼각김밥 클래스 예제와 스마트폰 예제가 이해가 좀 되었을까요? 부족한 머리로 예시를 많이 생각해서 만들어 보았는데, 여러분의 생각이 궁금합니다.
아 그리고, 파이썬 클래스에 대한 포스팅은 이게 끝이 아닙니다. 글이 길어져서 포스팅을 두개로 잘랐습니다. 다음시간에는 클래스 상속, 메서드 오버라이딩, 추상 클래스에 대해서 알아보겠습니다. 링크는 아래 있습니다.
감사합니다.
클래스 상속, 메서드 오버라이딩, 추상 클래스 [바로가기]
'<개인공부> > [Python]' 카테고리의 다른 글
[python] 파이썬 set (집합) 자료형 정리 및 예제 (0) | 2020.12.18 |
---|---|
[python] 파이썬 딕셔너리(dictionary) 자료형 정리 및 예제 (3) | 2020.12.13 |
[python] 파이썬 튜플(tuple)에 대해서 (0) | 2020.12.06 |
[Python] 파이썬 클래스2 상속, 추상 클래스, 메서드 오버라이딩 (0) | 2020.11.29 |
[python] 파이썬 함수 정리와 예제 (def) (5) | 2020.11.08 |
[python] 파이썬 리스트(list) 정리 및 예제 (2탄 응용편) (1) | 2020.10.29 |
[python] 파이썬 리스트(list) 정리 및 예제 (1탄 기본편) (19) | 2020.10.25 |
[python] 파이썬 eval 함수 정리 및 예제 (7) | 2020.10.22 |