class Init:
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
15wk-1: 클래스공부 5단계 – 상속
강의영상
youtube: https://youtube.com/playlist?list=PLQqh36zP38-zY0ccMvo01wQXc1Ht9H2TA
클래스공부 5단계: 상속
상속의 사용방법
-
클래스를 조금 수정하고 싶을때, 아래와 같은 문법을 이용하면 편리하다.
class 새로운_클래스_이름(수정할_클래스_이름):
def 수정_및_추가할_함수이름(self,...):
...
간단한 사용예시
class Show(Init):
def show(self):
## 여기는 Show 클래스야
print("Show클래스에서 정의된 show메소드를 실행합니다")
print('value={}'.format(self.value))
= Show(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
a.show()
Show클래스에서 정의된 show메소드를 실행합니다
value=5
a.show??
Signature: a.show() Docstring: <no docstring> Source: def show(self): ## 여기는 Show 클래스야 print("Show클래스에서 정의된 show메소드를 실행합니다") print('value={}'.format(self.value)) File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-342-5ac798f79ec3> Type: method
__init__?? a.
Signature: a.__init__(value) Docstring: Initialize self. See help(type(self)) for accurate signature. Source: def __init__(self,value): ## 여기는 Init 클래스야 print("Init클래스에서 정의된 __init__메소드를 실행합니다") self.value = value File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-341-3a7b8109daf8> Type: method
슈퍼클래스 메소드 재활용
-
방법1: 직접 슈퍼클래스 명시
class Deco(Show):
def __init__(self,value):
## 여기는 Deco클래스야~
print("짜라란~~")
__init__(self,value)
Show.print("짠짠~!!")
=Deco(5) a
짜라란~~
Init클래스에서 정의된 __init__메소드를 실행합니다
짠짠~!!
a.show??
Signature: a.show() Docstring: <no docstring> Source: def show(self): ## 여기는 Show 클래스야 print("Show클래스에서 정의된 show메소드를 실행합니다") print('value={}'.format(self.value)) File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-342-5ac798f79ec3> Type: method
__init__?? a.
Signature: a.__init__(value) Docstring: Initialize self. See help(type(self)) for accurate signature. Source: def __init__(self,value): ## 여기는 Deco클래스야~ print("짜라란~~") Show.__init__(self,value) print("짠짠~!!") File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-348-8837bbd4ea3b> Type: method
-
방법2: super() 이용 (생략안한버전)
class Deco(Show):
def __init__(self,value):
## 여기는 Deco클래스야~
print("짜라란~~")
super(Deco,self).__init__(value)
print("짠짠~!!")
=Deco(5) a
짜라란~~
Init클래스에서 정의된 __init__메소드를 실행합니다
짠짠~!!
a.show??
Signature: a.show() Docstring: <no docstring> Source: def show(self): ## 여기는 Show 클래스야 print("Show클래스에서 정의된 show메소드를 실행합니다") print('value={}'.format(self.value)) File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-342-5ac798f79ec3> Type: method
__init__?? a.
Signature: a.__init__(value) Docstring: Initialize self. See help(type(self)) for accurate signature. Source: def __init__(self,value): ## 여기는 Deco클래스야~ print("짜라란~~") super(Deco,self).__init__(value) print("짠짠~!!") File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-361-5ba5667ac964> Type: method
-
방법3: super() 이용 (생략한버젼) <– 이렇게 쓰세요!
class Deco(Show):
def __init__(self,value):
## 여기는 Deco클래스야~
print("짜라란~~")
super().__init__(value)
print("짠짠~!!")
=Deco(5) a
짜라란~~
Init클래스에서 정의된 __init__메소드를 실행합니다
짠짠~!!
a.show??
Signature: a.show() Docstring: <no docstring> Source: def show(self): ## 여기는 Show 클래스야 print("Show클래스에서 정의된 show메소드를 실행합니다") print('value={}'.format(self.value)) File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-342-5ac798f79ec3> Type: method
__init__?? a.
Signature: a.__init__(value) Docstring: Initialize self. See help(type(self)) for accurate signature. Source: def __init__(self,value): ## 여기는 Deco클래스야~ print("짜라란~~") super().__init__(value) print("짠짠~!!") File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-365-9c3108795053> Type: method
-
방법4: super() 이용, 방법3을 이해하기 위한 코드
class Deco(Show):
def __init__(self,value):
## 여기는 Deco클래스야~
print("짜라란~~")
super(__class__,self).__init__(value)
print("짠짠~!!")
=Deco(5) a
짜라란~~
Init클래스에서 정의된 __init__메소드를 실행합니다
짠짠~!!
a.show??
Signature: a.show() Docstring: <no docstring> Source: def show(self): ## 여기는 Show 클래스야 print("Show클래스에서 정의된 show메소드를 실행합니다") print('value={}'.format(self.value)) File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-342-5ac798f79ec3> Type: method
__init__?? a.
Signature: a.__init__(value) Docstring: Initialize self. See help(type(self)) for accurate signature. Source: def __init__(self,value): ## 여기는 Deco클래스야~ print("짜라란~~") super(__class__,self).__init__(value) print("짠짠~!!") File: ~/Dropbox/07_lectures/PP2023/posts/03_Class/<ipython-input-375-4e2b30e113c0> Type: method
-
이때 방법2-4는 완전히 동일한 코드이다, 방법1-4는 이 예제에서 같은효과이다.
다중상속
일반적인 다중상속
-
Add 클래스선언
class Add:
def __init__(self,value):
self.value = value
def __add__(self,value2):
return self.value + value2
=Add(2) a
+5 a
7
*2 # 곱하기는 정의한적없음 a
TypeError: unsupported operand type(s) for *: 'Add' and 'int'
-
Mul 클래스선언
class Mul:
def __init__(self,value):
self.value = value
def __mul__(self,value2):
return self.value * value2
= Mul(5)
a a.value
5
+2 #정의한적 없음 a
TypeError: unsupported operand type(s) for +: 'Mul' and 'int'
*2 a
10
-
AddMul 클래스를 선언 (기존의 Add, Mul 상속받아서 이용)
class AddMul(Add,Mul):
pass
= AddMul(5)
a a.value
5
+2 a
7
*5 a
25
다중상속 우선순위 (__init__
이 겹치는뎅?)
class Add:
def __init__(self,value):
print("Add클래스에서 정의된 __init__ 메소드가 실행됩니다")
self.value = value
def __add__(self,value2):
return self.value + value2
class Mul:
def __init__(self,value):
print("Mul클래스에서 정의된 __init__ 메소드가 실행됩니다")
self.value = value
def __mul__(self,value2):
return self.value * value2
class AddMul(Add,Mul):
pass
= AddMul(5) a
Add클래스에서 정의된 __init__ 메소드가 실행됩니다
믹스인 클래스 (\(\star\star\star\))
class Init:
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
class Add(Init):
def __add__(self,value2):
return self.value + value2
class Mul(Init):
def __mul__(self,value2):
return self.value * value2
class AddMul(Add,Mul):
pass
= AddMul(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
+2 a
7
*5 a
25
다중상속시 super()
의 활용
super()를 쓰지 않은 나쁜사용예시
-
초기값을 설정하는 클래스 만듬
class Init:
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
-
init을 상속받아서
- “\({\tt 초기값}= {\tt 초기값} \times 2\)”
- “\({\tt 초기값}= {\tt 초기값} + 5\)”
를 인스턴스 생성과 동시에 수행하는 클래스를 각각 만듦
class Times2(Init):
def __init__(self,value):
__init__(self,value)
Init.self.value = self.value * 2
=Times2(5)
a a.value
Init클래스에서 정의된 __init__메소드를 실행합니다
10
class Plus5(Init):
def __init__(self,value):
__init__(self,value)
Init.self.value = self.value + 5
=Plus5(5)
a a.value
Init클래스에서 정의된 __init__메소드를 실행합니다
10
-
지나고 보니까 “\({\tt 초기값} = {\tt 초기값} \times 2 + 5\)” 를 인스턴스 생성과 동시에 수행해주는 클래스를 만들고 싶음.
class Times2Plus5(Times2,Plus5):
def __init__(self,value):
__init__(self,value)
Times2.__init__(self,self.value) Plus5.
= Times2Plus5(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value
15
-
싫은이유1: 코드가 지저분하다.
-
싫은이유2: 진정한 의미의 상속이 아닌것 같다.
class Times2Plus5():
def __init__(self,value):
__init__(self,value)
Times2.__init__(self,self.value) Plus5.
= Times2Plus5(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value
15
super()를 활용한 좋은사용예시
-
아키텍처를 아래와 같이 바꾸자
class Init(object):
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
class Times2(Init):
def __init__(self,value):
super().__init__(value)
self.value = self.value * 2
class Plus5(Init):
def __init__(self,value):
super().__init__(value)
self.value = self.value + 5
class Times2Plus5(Plus5,Times2):
def __init__(self,value):
super().__init__(value)
-
써보자
=Times2Plus5(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value
15
5*2)+5 (
15
-
이것이 왜 가능?
원리: mro상으로 상위에 있는 순서대로 타고 올라간 뒤, mro순서대로 한번씩만
__init__
을 실행함!!
-
소감: 코드가 깔끔하긴해 + 진정한 상속의 느낌도 있어 (그렇지만 사용하고 싶지는 않음)
super()
의 사용방법
-
이제 “\(({\tt 초기값} \times 2 + 5)\times 2\)” 를 수행해주는 클래스를 만들고 싶음.
class Init(object):
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
class Times2(Init):
def __init__(self,value):
super().__init__(value)
self.value = self.value * 2
class Plus5(Init):
def __init__(self,value):
super().__init__(value)
self.value = self.value + 5
class Times2Plus5Times2(Plus5,Times2):
def __init__(self,value):
super().__init__(value)
super(Plus5,self).__init__(self.value)
=Times2Plus5Times2(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value
30
Times2Plus5Times2.mro()
[__main__.Times2Plus5Times2,
__main__.Plus5,
__main__.Times2,
__main__.Init,
object]
-
코드해석
super().__init__(value)
은Times2Plus5Times2
의 MRO순서로 상위인 클래스Init
,Times2
,Plus
의__init__
을 순서대로 실행하되 중복실행은 하지 않음.super(Plus5,self).__init__(self.value)
은Plus5
보다 MRO순서로 상위인 클래스Init,Times2
의__init__
을 순서대로 실행하되 중복실행은 하지 않음.
-
그냥 이게 낫지 않나?
class Init(object):
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value
class Times2(Init):
def times2(self):
self.value = self.value * 2
class Plus5(Init):
def plus5(self):
self.value = self.value + 5
class Times2Plus5Times2(Plus5,Times2):
def times2plus5times2(self):
self.times2()
self.plus5()
self.times2()
= Times2Plus5Times2(5) a
Init클래스에서 정의된 __init__메소드를 실행합니다
a.times2plus5times2()
a.value
30
-
그래도 이 문법을 알아야 한다. 왜??
단계 | 인터넷밈 | 클래스 | 레포트표지 |
---|---|---|---|
1단계: 구상 | \(\bullet\) 이거 재미있다. \(\bullet\) 밈화하자. |
\(\bullet\) 이 코드 반복해서 자주 쓸 것 같다. \(\bullet\) 이 코드를 쉽게 찍어내는 (복사할 수 있는) 클래스를 만들자 |
\(\bullet\) 레포트 표지를 자주 만들 것 같음 \(\bullet\) 양식파일을 만들까? |
2단계: 틀생성 | \(\bullet\) “밈틀”: 복사하고 싶은 속성을 추려 밈을 생산하기에 유리한 틀을 만듬 | \(\bullet\) 클래스의 선언 | \(\bullet\) REPORT_2023_최규빈.hwp 양식파일을 생성 |
3단계: 틀 \(\to\) 복제 | \(\bullet\) 밈화: “밈틀”에서 다양한 밈을 만들고 놈 | \(\bullet\) 인스턴스화: 클래스에서 인스턴스를 생산 | \(\bullet\) 레포트 양식표지에서 다양한 레포트를 냄 |
4단계: 틀 \(\to\) 틀변경 \(\to\) 복제 | \(\bullet\) 생각해보니까 초기 밈틀은 시시함. \(\bullet\) 초기 밈틀을 수정해 새로운 밈틀을 만들고 더 재미있는 밈을 만들고 놈 |
\(\bullet\) 초기클래스와 비슷한 클래스를 선언할 일이 생김 \(\bullet\) 상속,오버라이딩: 초기클래스를 상속받아 클래스를 새롭게 정의하고 인스턴스를 재 생산 |
\(\bullet\) 공모전에 참가하여 결과보고서를 작성할 일이 생김. \(\bullet\) REPORT_2023_최규빈.hwp 를 적당히 변형하여 수정된 틀을 만들고 결과보고서 생산. |
상속은 위의 표에서 4단계에 해당한다. 즉 어떠한 클래스를 상속받을때는 “내가 만든 클래스”가 아닐 경우가 대부분이다. 따라서 “애초부터 메소드가 겹치지 않게 클래스들을 깔끔하게 디자인을 하는것” 은 불가능한 경우가 많다.
리스트의 상속
-
list와 비슷한데 멤버들의 빈도가 계산되는 메소드를 포함하는 새로운 나만의 list를 만들고 싶다.
= list('asdfasssdfa')
lst lst
['a', 's', 'd', 'f', 'a', 's', 's', 's', 'd', 'f', 'a']
-
각 원소들의 빈도를 구해보면 아래와 같다.
for s in set(lst)} {s:lst.count(s)
{'d': 2, 'a': 3, 's': 4, 'f': 2}
# 이렇게 실행하면 위의결과가 나왔으면 좋겠다. lst.freq()
AttributeError: 'list' object has no attribute 'freq'
-
이것을 내가 정의하는 새로운 list의 메소드로 넣고 싶다.
class List(list):
def freq(self):
return {s:self.count(s) for s in set(self)}
= List('asdfasssdfa') lst2
lst2
['a', 's', 'd', 'f', 'a', 's', 's', 's', 'd', 'f', 'a']
lst
['a', 's', 'd', 'f', 'a', 's', 's', 's', 'd', 'f', 'a']
#lst2+lst ## 거의 lst2는 일반적인 lst와 같은역할
-
기존리스트에서 추가로 frequency() 메소드가 존재함.
lst2.freq()
{'d': 2, 'a': 3, 's': 4, 'f': 2}
숙제
import datetime
(1)
아래의 코드를 관찰하고 datetime의 동작을 유추하라.
= datetime.datetime.now().strftime('%y-%m-%d %X')
current_time current_time
'23-06-16 09:35:44'
(2)
아래의 코드를 관찰하고 Time 클래스의 기능을 추정하라.
class Time:
def time(self):
return datetime.datetime.now().strftime('%y-%m-%d %X')
= Time() time_ins
time_ins.time()
'23-06-16 09:35:45'
time_ins.time()
'23-06-16 09:35:45'
(3)
Time을 상속받아 Init 클래스를 만들고 __repr__
을 조작하여 아래와 같이 인스턴스 생성시점을 출력하는 기능을 구현하라.
= Init() a
a
인스턴스생성시점: 23-06-16 10:27:19
= Init() b
a,b
(인스턴스생성시점: 23-06-16 10:27:19, 인스턴스생성시점: 23-06-16 10:27:28)
(4)
아래는 Dummy 클래스이다.
class Dummy:
def __init__(self):
self.a = 1
self.b = 2
def dummy(self):
pass
= Dummy() dummy
dir(dummy)
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'a',
'b',
'dummy']
dummy 클래스에서 숨겨지지 않은 attribute는 ‘a’, ‘b’, ‘dummy’ 이다. 이를 출력하는 코드를 작성하라.
hint: 숨겨지지 않은 attribute는 _
로 시작하지 않는다.
for l in dir(dummy) if l[0]!='_'] [l
['a', 'b', 'dummy']
hint2 (5)번의 코드를 잘 관찰하세요..
(5)
아래클래스를 관찰하고 check()의 기능을 유추하라.
class Check:
def ckeck(self):
return [l for l in dir(self) if l[0]!='_']
= Check() ck
ck.ckeck()
['ckeck']
(6)
tuple 클래스와 Check를 상속받아 아래와 같은 역할을 하는 새로운 Tuple 클래스를 만들라.
= Tuple('asdfassdfsasdf')
tpl # 값과 함께 사용가능한 메소드가 함께 출력 tpl
('a', 's', 'd', 'f', 'a', 's', 's', 'd', 'f', 's', 'a', 's', 'd', 'f')
methods=['ckeck', 'count', 'freq', 'index']
tpl.freq()
{'d': 3, 'a': 3, 's': 5, 'f': 3}