Home Python 13 - Class 2
Post
Cancel

Python 13 - Class 2

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에서 값을 또 입력하는
      수고를 덜게됨
    • 아무튼 상황에 따라 골라쓸 수 있음.

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에서 구현 ‘가능’

This post is licensed under CC BY 4.0 by the author.

Python 12 - Class 1

Python 14 - File 1

Comments powered by Disqus.