class Init:
def __init__(self,value):
## 여기는 Init 클래스야
print("Init클래스에서 정의된 __init__메소드를 실행합니다")
self.value = value15wk-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))a = Show(5)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
a.__init__??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("짜라란~~")
Show.__init__(self,value)
print("짠짠~!!")a=Deco(5)짜라란~~
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
a.__init__??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("짠짠~!!")a=Deco(5)짜라란~~
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
a.__init__??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("짠짠~!!")a=Deco(5)짜라란~~
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
a.__init__??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("짠짠~!!")a=Deco(5)짜라란~~
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
a.__init__??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 + value2a=Add(2)a+57
a*2 # 곱하기는 정의한적없음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 * value2a = Mul(5)
a.value5
a+2 #정의한적 없음TypeError: unsupported operand type(s) for +: 'Mul' and 'int'
a*2 10
- AddMul 클래스를 선언 (기존의 Add, Mul 상속받아서 이용)
class AddMul(Add,Mul):
pass a = AddMul(5)
a.value5
a+27
a*525
다중상속 우선순위 (__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 a = AddMul(5)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 a = AddMul(5)Init클래스에서 정의된 __init__메소드를 실행합니다
a+27
a*525
다중상속시 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.__init__(self,value)
self.value = self.value * 2a=Times2(5)
a.valueInit클래스에서 정의된 __init__메소드를 실행합니다
10
class Plus5(Init):
def __init__(self,value):
Init.__init__(self,value)
self.value = self.value + 5a=Plus5(5)
a.valueInit클래스에서 정의된 __init__메소드를 실행합니다
10
- 지나고 보니까 “\({\tt 초기값} = {\tt 초기값} \times 2 + 5\)” 를 인스턴스 생성과 동시에 수행해주는 클래스를 만들고 싶음.
class Times2Plus5(Times2,Plus5):
def __init__(self,value):
Times2.__init__(self,value)
Plus5.__init__(self,self.value)a = Times2Plus5(5)Init클래스에서 정의된 __init__메소드를 실행합니다
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value 15
- 싫은이유1: 코드가 지저분하다.
- 싫은이유2: 진정한 의미의 상속이 아닌것 같다.
class Times2Plus5():
def __init__(self,value):
Times2.__init__(self,value)
Plus5.__init__(self,self.value)a = Times2Plus5(5)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)- 써보자
a=Times2Plus5(5)Init클래스에서 정의된 __init__메소드를 실행합니다
a.value15
(5*2)+515
- 이것이 왜 가능?
원리: 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)a=Times2Plus5Times2(5)Init클래스에서 정의된 __init__메소드를 실행합니다
Init클래스에서 정의된 __init__메소드를 실행합니다
a.value30
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()a = Times2Plus5Times2(5)Init클래스에서 정의된 __init__메소드를 실행합니다
a.times2plus5times2()a.value30
- 그래도 이 문법을 알아야 한다. 왜??
| 단계 | 인터넷밈 | 클래스 | 레포트표지 |
|---|---|---|---|
| 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를 만들고 싶다.
lst = list('asdfasssdfa')
lst ['a', 's', 'd', 'f', 'a', 's', 's', 's', 'd', 'f', 'a']
- 각 원소들의 빈도를 구해보면 아래와 같다.
{s:lst.count(s) for s in set(lst)}{'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)}lst2 = List('asdfasssdfa')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의 동작을 유추하라.
current_time = datetime.datetime.now().strftime('%y-%m-%d %X')
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_ins = Time()time_ins.time()'23-06-16 09:35:45'
time_ins.time()'23-06-16 09:35:45'
(3) Time을 상속받아 Init 클래스를 만들고 __repr__을 조작하여 아래와 같이 인스턴스 생성시점을 출력하는 기능을 구현하라.
a = Init()a인스턴스생성시점: 23-06-16 10:27:19
b = Init()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):
passdummy = 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는 _로 시작하지 않는다.
[l for l in dir(dummy) if l[0]!='_']['a', 'b', 'dummy']
hint2 (5)번의 코드를 잘 관찰하세요..
(5) 아래클래스를 관찰하고 check()의 기능을 유추하라.
class Check:
def ckeck(self):
return [l for l in dir(self) if l[0]!='_']ck = Check()ck.ckeck()['ckeck']
(6) tuple 클래스와 Check를 상속받아 아래와 같은 역할을 하는 새로운 Tuple 클래스를 만들라.
tpl = Tuple('asdfassdfsasdf')
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}