2019.03.21 TIL
(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)
# 질문에 답하기.
- 아래의 코드를 따라가면서 #에 따라 답해보기
- 3.
코드를 통해 보는 class
>>>
>>> def sum(a,b): #1
>>> return(a+b)
>>>
>>> class Account:
>>>
>>> interest_rate = 0.08 #2
>>> num_of_account = 0
>>>
>>>
>>> @staticmethod #3
>>> def func(a,b):
>>> return(a+b)
>>>
>>> @classmethod #4
>>> def string_constructor(cls, string):
>>> data = string.split('_')
>>> clnt_name = data[0]
>>> balance = int(data[1])
>>> return cls(clnt_name, balance)
>>>
>>> @classmethod #5
>>> def get_num_of_account(cls):
>>> """
>>> Account.get_num_of_account() ---> interger
>>> return : 개설된 계좌 수
>>> """
>>> return cls.num_of_account
>>>
>>> def __init__(self, clnt_name, balance): #6
>>> self.clnt_name = clnt_name #7
>>> self.balance = balance
>>> Account.num_of_account += 1
>>>
>>> def deposit(self, money): #8
>>> """
>>> a.deposit(money) --> boolean
>>> 만약에 money > 0 입금성공
>>> 아니면 에러 메시지 출력 후 실패!
>>> """
>>> if money < 0:
>>> print("입금은 0원 초과부터 가능합니다.")
>>> return False
>>> self.balance += money
>>> return True
>>>
>>> def withdraw(self, money):
>>> """
>>> a.withdraw(money) --> integer
>>> return : 인출된 돈
>>> 만약 잔고가 모자라면 None
>>> """
>>> if money > self.balance:
>>> print("인출할 돈이 잔액보다 많습니다.")
>>> return None
>>> self.balance -= money
>>> return money
>>>
>>> def transfer(self, other, money): #9
>>> self.balance -= money
>>> other.deposit(money)
>>>
>>> def __str__(self): #10
>>> return f"{self.clnt_name} : {self.balance}"
>>>
>>> if __name__ == "__main__"":
>>> my_acnt = Account("greg", 5000) #11
>>> your_acnt = Account("john", 2000)
>>>
>>> print(Account.interest_rate) #12
>>> print(Account.get_num_of_account()) #13
>>> print(my_acnt.interest_rate) #14
>>> print(my_acnt.get_num_of_account()) #15
>>> print(Account.func(4,5)) #16
>>> print(type(Account.interest_rate)) #17
>>> print(type(Account.get_num_of_account())) #18
>>> print(type(Account.get_num_of_account)) #19
>>> print(type(my_acnt.deposit)) #20
>>> print(type(Account.deposit)) #21
>>> print(type(Account.func)) #22
>>>
>>> s = 'james_6000' #23
>>> his_acnt = Account.string_constructor(s)
>>> print(his_acnt)
>>>
>>> my_acnt.deposit(7000) #24
>>> print(my_acnt)
>>>
>>> result1 = my_acnt.withdraw(3000) #25
>>> result2 = your_acnt.withdraw(3000)
>>> print(result1)
>>> print(result2)
>>>
>>> print(my_acnt)
>>> print(your_acnt)
>>>
>>>
0.08
2
0.08
2
9
<class 'float'>
<class 'int'>
<class 'method'>
<class 'method'>
<class 'function'>
james : 6000
greg : 12000
인출할 돈이 잔액보다 많습니다.
3000
None
greg : 9000
john : 2000
>>>
>>>
>>>
#에 따라 정리하기
- #1 전역 함수
- 어느 클래스에서도 속하지 않는다.
- oop에서 제일 싫어하는 행동이다.
- 최대한 관련있는 class에게 넣어주자.
- #2 클래스 멤버(class member)
- 모든 객체가 공유한다.
- 전역 변수(global variable)를 대체한다.
- 클래스 바로 밑에 아무것도 적지 않고 적어주면 된다.
- 모든 객체가 가지고 있을 필요는 없으나 모든 객체가 함께 공유해야 하는 것
- ex: 이자율
- 객체를 통해서도 클래스 멤버에 접근 가능하다.
- 접근 방법 :
- my_acnt.interest_rate : 객체를 통한 접근 방법
- Account.interest_rate : 클래스를 통한 접근 방법
- #3 static method
- 매서드처럼 보이지만 대놓고 함수이다. 전역함수
- 전역 함수를 대체할 때 클래스메서드와 하나의 해결책이 될 수 있다.
- 인자로 클래스나 객체를 받지 않는다. 함수의 정의만 클래스 A의 네임 스페이스에 있을 뿐 일반 함수와 같다.
- #4 클래스메서드(class method == @데코레이터)
- 객체가 하나도 없는 상태에서도 호출이 가능하다.
- cls가 self 대신 메모리 주소를 받는다.
- 전역함수를 대체한다.
- 전역함수를 대체할 때는 static method를 쓸 수 있다.
- 아무것도 받지 않는다. 순수하게 함수다.
- 사용방법(Account.func(5,4))
- 전역함수를 대체할 때는 static method를 쓸 수 있다.
- 관용적으로 cls를 받는다.
- cls는 class를 받으므로 __init__ 와 같다.
- #5 클래스메서드(class method) + 대체 생성자
- 객체가 하나도 없는 상태에서도 호출이 가능하다.
- 전역 함수를 대체할 수 있다.
- 원래 파이썬은 하나의 생성자만을 허용하지만 클래스메서드를 통해 해결 가능하다.
- 대체 생성자(alternative constructor)가 될 수 있다.
- 파이썬에서는 생성자를 하나만 가능하기때문에 여러가지 형태의 입력에 대응 할수 없다.
- ex) 단체가입 “이름”_“초기값” —> s = ‘james_6000’
- 접근방법
- Account.get_num_of_account()
- 객체를 통해서도 클래스 메서드에 접근이 가능하다.
- #6 생성자(constructor)
- 파이썬에서는 오직 하나 존재한다.
- __init__ : 객체(object)가 생성될 때 반듯이!! 한번 호출된다.
- __ __ 은 파이썬에서 미리 쓰기로 해놓은 것이다.
- #7 인스턴스 멤버(instance member)
- 인스턴스 멤버 == 상태정보 관리이다.
- 각자의 객체가 가지는 값(값 = 상태정보)
- __init__에 보면 변수가 나와 있다.
- 생성된 object들은 같은 속성을 가지고 있으나 속성 값이 다르다.
- Account.num_of_account —> class멤버에 접근하는 방법
- #8 인스턴스 매서드(instance method)
- object의 메서드로 봐도 된다.
- class 입장에서는 function/ object입장에서는 메서드이다.
- 객체가 할 수 있는 행동, 기능을 이야기 한다.
- 인스턴스 메서드는 self를 꼭 써줘야 한다.
- self는 해당 객체가 저장된 첫번째 주소를 가르킨다.
- 즉 self는 생성된 객체 자기 자신을 가르킨다.
- my_account.deposit(5000)이 실행될 떄 my_account를 self로 던져준다. self는 객체의 메모리 주소를 참조한다. 즉 자체참조이다.
- #9 message passing(메시지 패싱)
- 나의 상태 정보와 함께 다른 객체의 상태 정보를 함께 변경해야 할 때
- 다른 객체의 상태 정보(인스턴스 멤버)를 변경할 때는 반드시 상대 객체가 가진 메서드를 이용해야 한다.
- 절대 하면 안되는 예)
- self.balance -= money
- other.balance += money
- 다른 객체가 가진 메서드를 이용하지 않았다.
- #10 __str__(self)
- my_acnt의 반환 포맷을 적어준다.
- 이게 없으면 __main__.Account object at 0x10f512438처럼 반환
- #11 객체 생성
- my_acnt(변수) = Account(“greg”, 5000) ==> class(매개변수)
- 변수 = class(매개변수) 형태로 생성
- 해당 객체는 해당 class의 인스턴스이다.
#12 클래스 멤버 접근 방법
- #13 클래스 메서드 접근 방법
- 클래스 메서드는 클래스 입장에서는 메서드지만(클래스의 행동 특정)
- class입장에서 deposit과 같은 것은 함수이다.(클래스에 영상 x)
- 하지만 객체 입장에서 deposit은 메서드이다.
- print(type(my_acnt.deposit)) — class ‘method’
- print(type(Account.deposit)) — class ‘function’
#14 객체를 활용한 클래스 멤버 접근 방법
#15 객체를 활용한 클래스 메서드 접근 방법
#16 static method 접근 방법
#17 클래스 멤버의 type
#18 클래스 메서드 실행 했을 때의 type —> int
#19 클래스 메서드의 type
#20 객체에서 메서드의 type
#21 클래스에서 인스턴스 메서드의 type
#22 static method의 타입
- #23 class method를 통해 대체 생성자를 만든 이유
- 상황에 따라 전혀 예상하지 못한 형태의 값이 주어질 수도 있음
- #24 instance method 호출 방법(객체.method())
- 객체에 self 인자는 넣어 줄 필요 없다.
- class를 만들 때는 꼭 self를 써줘야 하지만 method를 호출하거나 객체를 생성할 때는 쓸 필요가 없다.
- #25 함수와 method의 차이점을 보여주는 예
- 원래 withdraw라는 함수라면 입력이 3000원으로 같으므로 똑같이 나와야 한다.
- 하지만 메소드는 내부에 가지고 있는 상태 정보가 다르기 때문에는 다르게 나온다.
- 클로저에는 매개변수는 free variable이라고 부르는데
- OOP에서는 인스턴스 멤버라고 부르는 것을 통해 상태정보를 관리한다.
- 메서드 : 입력 + 인스턴스 멤버(상태정보, 데이터)에 의해 결과 값이 결정
- 인스턴스 멤버(상태정보)를 바꾸는 역할을 하기도 한다.
- 함수 : 입력에 의해 출력이 결정
- 메서드처럼 사용하기 위해서는 call by reference 혹은 다른 변수에 할당
- 메시지 패싱
- 객체 간의 상호 작용 –> 객체가 가지고 있는 멤버 값(데이터, 상태 정보)의 변화
- 내가 가진 건 1000이 적어지면 너에게 1000이 늘어난다.
- interaction이 반드시 메서드에 의해 이루어져야 한다.
- .은 속성을 가지고 온다는 뜻이다. 속성에는 인스턴스 멤버와 인스턴스 메서드가 있다.
절차지향 vs 객체지향
###procedural vs object-oriented
2개다 추상화를 하는 기법이다.
procedural : 함수를 이용해 추상화를 했다. 추상화의 도구로 함수를 이용 (절차지향)
- 이 프로그램은 어떤 일을 하는가?
- 기계와 비슷한 사고 방식
- 함수를 이용
object-oriented : 객체를 통해 추상화를 했다.
- 현실 세계(에 존재하는 객체(object)를 어떻게 모델링(modeling)할 것인가?
- 객체(object) : 사람, 동물, 자동차 등을 어떻게 프로그램으로 모델링 할 것인가?
- 사람과 비슷한 사고 방식
- 객체를 이용
객체 vs 인스턴스
메모리 상으로는 완벽히 같다. 하나를 볼 때는 객체로 보고 이 객체는 특정 클래스의 인스턴스라고 부른다.
why? 어떤 클래스에서 나왔는지 알고 싶을 때 - 클래스에 집중해서 말할 때
object란?
- 관련 있는 변수와 함수를 한곳에 모아(bundling) 놓은 곳(메모리의 영역)
- 변수 - 상태정보
- “관련 있는” 변수, 함수” “한곳에 모아” 가 중요하다.
- class를 통해 생성되므로 class의 속성을 가지고 있어야 한다.
- 속성
- instance member(변수)
- 각자의 객체가 가지는 값(값 = 상태정보)
- __init__에 보면 변수가 나와 있음
- 같은 속성을 가지고 있으나 속성 값이 다르다.
- instance method(함수)
- 객체가 할 수 있는 행동, 기능
- instance member(변수)
—> 절차지향에서는 전체 프로그램을 다 봐야하지만.
—> 객체지향에서는 단위로 프로그램을 유추해볼 수 있다.
중요한 이해
클래스의 메모리와 객체의 메모리는 모두 다르다.
객체마다 모두 다른 메모리 공간을 가지고 있다.
각 객체가 인스턴스 멤버를 따로 가지고 있고 메서드 역시 각자 가지고 있다고 생각하는게 좋다.
my_acnt
balance / clnt_name / deposit / withdraw / transfer
your_acnt
balance / clnt_name / deposit / withdraw / transfer
미션 : 클래스를 쓰지 않고 OOP 구현
클래스를 쓰지 않고.
(클래스 멤버, 메소드 제외).
어떤 객체를 만들 수 있어야 하고
그 객체에 대해 함수를 호출하여 돈을 입금
init / deposit / withdraw / transfer.
하나의 unit을 딕셔너리로 고정한다.
class라는 유닛 대신에 딕셔너리로 묶어라.
def person_init(name, money):
obj = {'name': name, 'money': money}
obj['give_money'] = Person[1]
obj['get_money'] = Person[2]
obj['show'] = Person[3]
print(obj)
# ---> obj = {'name' : name, 'money' : money , 'give_money' : Person[1], 'get_money' : Person[2], 'show' : Person[3]}
return obj
def give_money(self, other, money): # 3
self['money'] -= money
other['get_money'](other, money) # 4
def get_money(self, money):
self['money'] += money
def show(self):
print('{} : {}'.format(self['name'], self['money']))
Person = person_init, give_money, get_money, show
if __name__ == "__main__":
# 객체 생성
g = Person[0]('greg', 5000) # 5
j = Person[0]('john', 2000)
g['show'](g)
j['show'](j)
print('')
# 메시지 패싱
g['give_money'](g, j, 2000) # 6
g['show'](g)
j['show'](j)