[Python] 객체지향 프로그래밍
컴퓨터 사이언스 부트캠프 with 파이썬을 보고 정리한 내용입니다.
절차 지향 프로그래밍
- 절차 = 프로시저(procedure)는 서브 루틴, 메서드, 함수라고도 불림 = 간단하게 함수라고 하자.
- 함수는
- 입력을 받아 일련의 연산과정을 거쳐 출력을 내보냄
- 한번 정의해두면 어디서든 다시 사용할 수 있고 이름만 봐도 어떤 일을 하는지 알 수 있음
- 사용하는 사람이 만든 사람처럼 내부 구현을 할 필요가 없음 (= 다른 프로그래머도 쉽게 프로그램을 이해하고 유지보수 할 수 있음)
객체 지향 프로그래밍 (OOP)
- 객체(object) 지향 프로그래밍은 변수와 함수를 이용해 현실 세계에 존재하는 객체를 모델링함
캡슐화
- 우리는 모두 ‘사람’이라는 계측(클래스)에 속함
- 각자 키, 몸무게, 국적, 성별 등 같은 특성을 갖고 있지만 그 값은 다름 → 이 특성 값은 변수로
- 다들 잠자기, 숨쉬기, 말하기, 먹기 등의 행동을 할 수 있음 → 이 행동/기능은 함수로
- 이 변수(데이터)와 함수를 하나의 단위(클래스)로 묶는다 = 캡슐화 (encapsulation)
1
2
3
4
5
6
7
def person_init(name, money):
obj = {'name':name, 'money':money}
obj['give_money'] = Person[1]
obj['get_money'] = Person[2]
obj['show'] = Person[3]
return obj
클래스를 사용한 객체 만들기
- 클래스는 객체를 생성해 내는 템플릿이고 객체는 클래스를 이용해 만들어진 변수와 함수를 가진 메모리 공간
- 서로 다른 존재이며 메모리 공간도 다름
- 객체와 매우 유사한 개념으로 인스턴스가 있음
- 객체는 객체 자체에 초점을 맞춘 용어고, 인스턴스는 이 객체가 어떤 클래스에서 만들어졌는지에 초점을 맞춤 → ‘이 객체는 Person이라는 클래스의 인스턴스야’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
def __init__(self, name, money):
self.name = name
self.money = money
def give_money(self, other, money):
self.money -= money
other.get_money(money)
def get_money(self, money):
self.money += money
def show(self):
print('{} : {}'.format(self.name, self.money))
- OOP에서는
- 클래스로 묶이는 변수를 프로퍼티 / 멤버 변수 / 멤버라고 함 (*파이썬에서는 멤버)
- 객체가 가지는 멤버를 인스턴스 멤버라고 함
- 클래스에 묶이는 함수를 행동 / 멤버 함수 / 메서드라고 함(*파이썬에서는 메서드)
- 멤버 + 메서드를 합쳐서 속성(attribute)라고 함
- 클래스로 묶이는 변수를 프로퍼티 / 멤버 변수 / 멤버라고 함 (*파이썬에서는 멤버)
__init__
은 constructor 라고 부르는 특별한 함수로, 인스턴스 멤버를 초기화하는 역할- self 는 객체 자신을 의미함
- 생성 중인 객체에 name과 money 라는 멤버를 만들고 전달받은 인자들로 할당한다.
give_money()
,get_money()
,show()
함수는 모두 객체가 갖게 될 메서드임
1
2
3
4
5
6
7
8
9
if __name__ = ="__main__":
g = Person('greg', 5000)
j = Person('john', 2000)
g.show()
j.show()
g.give_money(j, 2000)
#인스턴스 메서드를 호출하면 객체가 자동으로 첫번째 인자인 self로 객체 자기 자신을 전달함
클래스 살펴보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type(Person.__init__)
type(Person.give_money)
type(Person.get_money)
#Person 클래스에 있는 것은 모두 함수임
type(g.give_money)
#객체의 메서드는 함수가 아닌 메서드로 출력됨
dir(g.give_money)
#이 메서드의 attribute 확인, __func__, __self__가 보임
g.give_money.__self__ # 이 메서드를 가진 자기 자신을 참조하고 있음
g.give_money.__self__ is g # True
#메서드 내부에 함수와 객체의 참조를 가지고 있으므로 함수에 직접 객체 참조를 전달할 수 있음
- 객체가 멤버와 메서드를 가질 수 있는 것처럼 클래스도 멤버와 메서드를 가질 수 있음
- 객체가 없어도 클래스를 통해 접근/호출할 수 있음
- 객체를 통해서도 접근/호출 가능
1
2
3
4
5
6
7
8
9
10
11
12
class A:
c_mem = 10 #클래스 멤버 <-> 인스턴스멤버와 다름
@classmethod #데코레이터를 사용해서 클래스 메서드를 만듦
def cls_f(cls)
print(cls.c_mem)
def __init__(self, num):
self.i_mem = num
def ins_f(self):
print(self.i_mem)
객체 지향 - 입출금 프로그램 만들기
- 은행에서 계좌 클래스를 관리하면서 총 몇개 개설되었는지를 클래스 멤버로 두어 인스턴스를 만들 때마다 하나씩 늘리자.
- 단, 계좌를 이용하는 고객은 개설 계좌 수를 인스턴스 멤버로 가질 필요가 없음
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
class Account:
num_act = 0
@classmethod
def get_num_acnt(clas):
return cls.num_acnt
def __init__(self, name, money):
self.user = name
self.balance = money
Account.num_acnt += 1 #계좌 하나 개설할 때마다 1씩 증가
def deposit(self, money)
if money < 0:
return
self.balance += money
def withdraw(self, money):
if money >0 and money <= self.balance:
self.balance -= money
return money
else:
return None
def transfer(self,other, moeny):
mon = slef.withdraw(money)
if mon:
other.deposit(mon)
return True
else:
return False
def __str__(self):
return 'user : {}, balance : {}'.format(self.user, self.balance)
정보 은닉
- 캡슐화할 때 어떤 멤버와 메서드는 공개하여 유저 프로그래머가 사용할 수 있도록 하고 어떤 멤버와 메서드는 숨길지 정해야 하는데 이런 개념을 정보 은닉(information hiding)이라고 함
- 숨길 경우, 메서드와 멤버가 클래스 안에서만 사용할 수 있고 객체를 통해서는 접근하거나 호출할 수 없게 됨
- 숨기는 이유?
- set_balance라는 메서드가 있고 이 메서드는 Account 클래스의 balance 값을 바꿀 수 있지만, balance가 음수인 것은 허용하지 않음 → 이를 액세스 함수라고 함
- balance를 숨겨놓으면, 유저 프로그래머가 직접적으로 balance를 바꿀 수 없고 오직 set_balance를 이용해야만 하게 됨 (음수를 실수로 입력하는 것을 방지)
- 숨기는 이유?
- 단 파이썬은 기본적으로 정보 은닉을 지원하지 않음(완벽한 정보 은닉 불가)
- 다만 유저 프로그래머의 실수를 막을 수 있는 방법을 2가지 제공함
1- 숨기려는 멤버 앞에 언더바 두개 붙이기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Account:
def __init__(self, name, money):
self.user = name
self.__balance = money
def get_balance(self):
return self.__balance
def set_balance(self, money):
if money < 0:
return
slef.__balance = money
if __name__ == "__main__":
my_acnt = Account('greg', 5000)
my_acnt.__balance = -3000 #직접 입력(실수)
print(my_acnt.get_balance())
#5000이 출력되고, 실수로 입력한 -3000은 반영되지 않았음
- 만약 언더바 두개를 멤버 앞에 붙이면, 이 멤버는 객체가 만들어질 때 이름이 변함 (
_클래스 이름
이 멤버 앞에 붙게 됨) - 즉
__balance
는_Account__balance
가 되는데, 다음과 같이 호출할 수 있음
1
2
my_acnt.__dict__
#{'user': 'greg', '_Account__balance':5000, '__balance':-3000}
- 즉 바꿀려면 바꿀수도 있다..!
2- 프로퍼티 기법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Account:
def __init__(self, name, money):
self.user = name
self.balance = money
#멤버가 아닌 setter 메서드
@property
def balance(slef):
return self._balance
@balance.setter
def balance(self, money):
if money < 0:
return
slef._balance = money
if __name__ == "__main__":
my_acnt = Account('greg', 5000)
my_acnt.balance = -3000 #직접 입력(실수)
print(my_acnt.balance())
- 데코레이터
@property
를 붙이는 순간balance()
는 getter 함수가 되어 이전 버전의get_balance()
메서드와 같은 역할을 함 - 이름이 같은 다른 함수는
@balance.setter
를 붙여 setter 함수로 쓰고,set_balance()
메서드와 같은 역할을 함 - 유저 프로그래머가 객체를 만들어 접근할 때는 getter, setter의 이름인
balance
를 마치 멤버인 것처럼 사용하게 됨 → 멤버에 직접 접근하는 것 같지만 사실은 getter, setter 함수를 호출해 실제 멤버인_balance
에 접근함 - 단 이 방법의 경우에도 다음과 같이 호출하면,
1
2
3
my_acnt.__dict__
#{'user':'greg', '_balance':5000}
my_acnt._balance = -3000 #이렇게 직접 지정하면 바뀜..
This post is licensed under CC BY 4.0 by the author.