Inheritance
- 부모자식의 시작.
- 목적이야뭐..
- 기존 class 확장.
- 새 field를 추가
- method 다시 정의
또는 새로 추가 등등. - 이건 oop 관련
다시 정리하면 좋을듯.
Basic
기본적으로 쓰는건
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
class parent1: a = None b = None def __init__(self): self.a = 1 self.b = 2 print("parent1") def mtd(self): print("parent1 mtd") class parent2: c = None d = None def __init__(self, c, d): self.c = c self.d = d print("parent2") class child(parent1, parent2): e = None f = None def __init__(self): parent1.__init__(self) parent2.__init__(self, 3, 4) self.e = 5 self.f = 6 print("child") def ps(self): print(self.a, self.b, self.c, self.d, self.e, self.f) def mtd(self): print("now child mtd") parent1.mtd(self) ch = child() ch.ps() ch.mtd()
이걸 기본으로 쓰겠음.
출력은1 2 3 4 5 6
parent1 parent2 child 1 2 3 4 5 6 now child mtd parent1 mtd
- 기본 class는
className:로 끝났다면
이름뒤에(parentClass, ...)를 붙임. - multi inheritance도 된다.
parent.init
- 상속 관계에서 child에는
parent의 field의 값이 들어오지 않음 parent1에서a,b에 값을 넣어도
원래는 상속받은child에 그 값이 전달되지 않음
그러니까 위에서1 2
parent1.__init__(self) parent2.__init__(self, 3, 4)
이게 빠지면 출력이
1 2
child None None None None 5 6
이렇게 됨
- 다만,
parent.__init__()가 빠져도
property나 method가 안들어오는건 아님
다시말해 상속은 됐는데
parent의 init에서 설정한 값들만 안들어옴. - 이걸 child입장에서 보면,
- parent에서 설정한 값들이 필요 없으면
parent.__init__를 할 필요 없어지고 - parent에서 설정한 값들을 쓰고싶으면
parent.__init__를 쓰면됨. 이 경우 child에서 값을 또 입력하는
수고를 덜게됨 - 아무튼 상황에 따라 골라쓸 수 있음.
- parent에서 설정한 값들이 필요 없으면
Overriding
위의 경우에서 상속만 받았다면
1
ch.mtd()
의 결과는
1
parent1 mtd
이건뭐..
overriding도 간단한데 child에서 처럼
1 2 3
def mtd(self): print("now child mtd") parent1.mtd(self)
이렇게 추가만 하면 됨.
이게 추가되서 결과적으로1 2
now child mtd parent1 mtd
이런 출력이 나옴.
여기에
parent1.mtd(self)를 더 써놨는데
이건 사실 필요없고
parent의 원래 mtd불러올때 이런식으로 하면 됨.
Multi Inheritance
- 이거에 관련된 설명을 좀 봐서 써봄
- 일단은 Multi Inheritance를
cpp는 허용인데 java, c#은 아님.
해서 사실 이 문제를 접할 상황이 없긴 했음. 아무튼 multi가 된다고는하고,
이 키워드랑 가장 관련있게 나오는게
The diamond problem인데1 2 3 4 5
- parent1 - / \ grandparent - - child \ / - parent2 -이런 구조일때를 말함.
보통 말하는건
grandparent에 있던 method는
child에서 어떻게 보일까
에 대한 문제로이름이 같은 method가
상속관계 전반에 걸쳐있을 때
모호함에 대한걸 말하는것같다.실제 저런 구조로
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
class gparent: gp = None def __init__(self): pass def gmtd(self): print("gparent") class parent1(gparent): a = None b = None def __init__(self): self.a = 1 self.b = 2 self.gp = 'from parent1' print("parent1") def mtd(self): print("parent1 mtd") def gmtd(self): print("parent1 gmtd") class parent2(gparent): c = None d = None def __init__(self, c, d): self.c = c self.d = d self.gp = 'from parent2' print("parent2") def gmtd(self): print("parent2 gmtd") class child(parent1, parent2): e = None f = None def __init__(self): parent1.__init__(self) parent2.__init__(self, 3, 4) self.e = 5 self.f = 6 print("child") def ps(self): # print(self.a, self.b, self.c, self.d, self.e, self.f) print(self.gp) def mtd(self): print("now child mtd") parent1.mtd(self)
이떄
1
ch.gmtd()
에 대한 출력은
1
parent1 gmtd
parent1, parent2모두를 상속받았으나
1을 참조하는것처럼 보인다.
method resolution order
- python은 이런 상속관계에서
method 참조 순서를 보여준다. class.mro()를 하면 볼 수 있다.1
print(child.mro())
를 하면
1 2 3 4
[<class '__main__.child'>, <class '__main__.parent1'>, <class '__main__.parent2'>, <class '__main__.gparent'>, <class 'object'>]
이렇게 나오는데
child에서 method를 참조하는 순서를 보여준다.순서상 먼저 써있는 class의 method를 참조하는데
child.gmtd()를 호출하면 child에서 먼저
gmtd()를 찾는데 위의 경우 child에 없다.그 다음 순서인 parent1의 gmtd()를 불러온것.
parent1의 gmtd()를 지운다면
gparent에서 불러온다.- 상속…이 복잡할 떄 method호출 관련
애매하면 이걸 보는 방법이 있다고 하드라..
여기서 뭘 바꾸거나 할수 있는건 아닌것같다.
super
- 아까 상속에서 parent의 init를 쓸때
paren1.__init__,parent2.__init__를
직접 명시했는데 super()을 넣어도 됨 - 이렇게 쓸 경우 두 parent중 첫번째로 쓴 parent를 호출함.
- parent를 하나쓸때는 상관 없어보이는데
2개 이상일때는 직접 명시하는게 좋아보인다.
Abstract class
- 일단 python에는 interface나 abstract이
딱히 없는것같다. 지금까지 찾은바로는.
in C#
비교해보면
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
interface IMyInterface { void MyMethodInterface(); } public abstract class MyBaseClass { int i; public abstract void MyMethodAbstract(); public virtual void MyMethodAbstract2() { Console.WriteLine("MyBaseClass.MyMethod"); } } public class MyClass : MyBaseClass, IMyInterface { public override void MyMethodAbstract() { throw new NotImplementedException(); } public override void MyMethodAbstract2() { base.MyMethodAbstract2(); } public void MyMethodInterface() { throw new System.NotImplementedException(); } }
근데 interface나 abstract등등을
관련되서 쓰는걸 보면 먼저 틀만 잡아놓고,
상속받고, 구현하는게 비슷하긴함
다만 조금씩 재약이 다르고
좀 디테일하게 쓰는 방법이 다른정도?그렇게보면 이런 구별 없이 쓰는게
좀 나은건가 싶기도 하고…
어차피 상속-구현이 중점이니까.- 아무튼 중요한점은 interface, abstract를
상속받는 class에서 method의 구현을
강제할수 있냐없냐가 중요한듯하다. - C#에서 interface는 무조건 구현을 강제하지만
abstract는virtual,abstract로 구별해
선택적으로 가능해진다.
pass
method를 구현 안하고
비워둘수 있는 키워드1 2
class parent: def method1(self):
이렇게 쓸 수 없음.
1 2 3
class parent: def method1(self): pass
구현 안하고 넘어가는 method에 쓰면됨.
in python
python에서는
1 2 3 4 5 6 7 8 9
class parent: def method_1(self): pass class child(parent): def method_2(self): print("child method2") c = child()
이렇게 해도 아무 문제 없다.
- override가 키워드 제약없이 쉬워보였는데
상속받는 clss에 강제하는 방법도 없어보임. 조금 바꿔서
1 2 3 4 5 6 7 8 9 10
class parent: def method_1(self): raise NotImplementedError() class child(parent): def method_2(self): print("child method2") c = child() c.method_1()
이런식으로 exception을 발생시켜
이후에 구현을 유도할수도 있지만…문제는
NotImplementedError()가
포함된 method를 호출하기 전까지
아무 문제없이 실행된다는점이다.위에서
c.method_1()가 없으면
아무 문제없이 넘어갈수도 있게됨.- 간단한 예시만 보면
그냥 알잘딱깔센 하면 되지 않을까 싶은데
이게 언제나 점점 덩치가 불어나면 문제가 된다.
ABC(Abstract Base Class)
- python에서 이런 문제를 해결하기위해 쓴다.
- parent를 상속받는 child에서
구현을 강제한다. abc를 써보면
1 2 3 4 5 6 7 8 9 10
import abc class parent(metaclass=abc.ABCMeta): @abc.abstractmethod def method_1(self): pass def method_2(self): pass
metaclass=abc.ABCMeta와
@abc.abstractmethod가 생겼다.metaclass=abc.ABCMeta는
abstract class라고 알리는것 같은데
이 키워드가 붙으면 해당 class로는 더아상
instance를 만들 수 없다.1
c1 = parent()
를 하면
1
TypeError: Can't instantiate abstract class parent with abstract method method_1
라고 한다. 암튼 안됨.
@abc.abstractmethod는
상속 받는 child class에서
강제로 구현을 하도록 하는데이 전에 썼던
NotImplementedError()와 다른점은
NotImplementedError()은 exception시점이
해당 method가 호풀될 때 이지만
@abc.abstractmethod를 쓰면 런타임때 걸림.1
TypeError: Can't instantiate abstract class child with abstract method method_1
…근데 내용은 같네…
아무튼 결과적으로
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import abc class parent(metaclass=abc.ABCMeta): @abc.abstractmethod def method_1(self): pass def method_2(self): pass class child(parent): def method_ch(self): print("child method") def method_1(self): print("child method 1") c = child()
이런 형태가됨.
@abc.abstractmethod가 안붙은 method는
이후 구현을 같제하지 않음. 하던말던.
vs C#
- C#이랑좀 비교해보면 요정도
C#
virtual
child class에서 구현 ‘가능’abstract
child class에서 구현 ‘강제’두 키워드 없는 일반적인 경우
child class에서 구현 ‘불가’python
@abc.abstractmethod
child class에서 구현 ‘강제’키워드 없는 일반적인 경우
child class에서 구현 ‘가능’
Comments powered by Disqus.