객체 지향 프로그래밍(OOP) - 상속

파이썬 테스트

  • Problem 1

    • n번째 피보나치 수를 반환하는 함수를 재귀 함수와 반복문을 활용한 함수로 만들기

    • 사용자가 매개변수로 반드시 1 이상의 수를 넣음(에러 핸들링 X)

    • 출력 결과 >> 0 1 1 2 3 5 8 13 21 34

    • 만약 n이 4이면 반환 값은 2

      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
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      # 재귀 함수 사용
      def fibo_recursion(n):

      if n <= 1:
      return 0
      elif n == 2:
      return 1

      return fibo_recursion(n - 2) + fibo_recursion(n - 1)

      #꼬리 재귀 --> while 문으로 치환 가능


      # 반복문 사용 - while
      def fibo_iteration_while(n):

      a = 0
      b = 1
      count = 1

      while not count == n:

      a, b = b, a + b
      count += 1

      return a


      # 반복문 사용 - for
      def fibo_iteration_for(n):

      a = 0
      b = 1

      for _ in range(n - 1):
      a, b = b, a + b

      return a


      # 출력 확인
      if __name__ == "__main__":
      for i in range(1, 11):
      # 결과 값은
      # 0 1 1 2 3 5 8 13 21 34
      print(fibo_recursion(i), end = " ")
      print("")

      for i in range(1, 11):
      print(fibo_iteration_while(i), end = " ")
      print("")

      for i in range(1, 11):
      print(fibo_iteration_for(i), end = " ")

      >> 0 1 1 2 3 5 8 13 21 34
      0 1 1 2 3 5 8 13 21 34
      0 1 1 2 3 5 8 13 21 34
  • Problem 2

    • 피보나치 함수를 재귀함수를 통해 구현하되 내부에 캐시로 사용할 자료구조를 둠

    • 한번 호출되었던 값은 캐시에 저장

    • 다음번에 다시 같은 매개변수가 전달되면 연산하지 않고 캐시에 저장된 값을 바로 반환하도록 구현

    • 캐시로 사용할 자료구조는 리스트로 한정하며 리스트는 [0, 1]부터 시작

    • 클로저를 이용해 구현

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      def make_fibo():
      fibo_list = [0, 1]
      def inner(n):
      if len(fibo_list) < n:
      fibo_list.append(inner(n - 2) + inner(n - 1))
      return fibo_list[n - 1]

      return inner

      # 위와 같이 코딩하는 것을 Memoiztion --> dynamic programming이라 한다.

      if __name__ == "__main__":
      fibo = make_fibo()
      # print(fibo.__name__)
      for i in range(1, 11):
      print(fibo(i), end = " ")

      >> 0 1 1 2 3 5 8 13 21 34

OOP 상속

  • parent <-> child, super <-> sub, base <-> denied

  • attribute = member, method

  • OOP에서는 외부에서 member의 변수에 접근하는 것은 절대 하지 않아야 한다.

  • 기능을 추가한 클래스 말고 다른 클래스들은 영향을 받지 않음(Open - Closed)

  • 클래스들과의 관계가 OOP에서 제일 중요(relations between closes)

    • interitance(상속)

    • composition = composition(통합), aggregation(집합)

  • Modeling

    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    # modeling : 내가 만들고 있는 프로그램에서 필요한 만큼의 속성만 구현

    class Person:
    def __init__(self, name, money):
    # 다른 메서드에서 인스턴스 멤버를 만들 수 있지만,
    # 되도록이면 생성자에 넣어주도록 한다.(그 값이 나중에 바뀔지라도)
    self.name = name
    self.money = money

    # getter
    @property
    def money(self):
    print("getter executed")
    return self.__money

    @money.setter
    # money처럼 반드시 이름을 맞춰주어야 한다.
    def money(self, money):
    print("setter executed")
    if money < 0:
    try:
    self.__money = self.__money
    except AttributeError:
    self.__money = 0
    finally:
    return

    self.__money = money

    # property는 반드시 메서드의 이름을 맞춰준다.

    # access function
    # getter
    # def get_money(self):
    # return self.money

    # access function
    # setter
    # def set_money(self, money):
    # self.money = money


    if __name__ == "__main__":

    #생성자에 음수가 기입됐을 때
    john = Person('john', -5000)

    # 객체의 멤버에 접근(getter)이나 수정(setter)을 할 때는
    # 반드시 메서드(access function)를 이용한다!!
    # print(john.get_money())
    # john.set_money(4000)

    # 유저 프로그래머는 객체의 멤버처럼 취급하면 된다.
    # 하지만 내부적으로는 getter 혹시 setter를 호출한다.
    # 반드시 메서드를 통해 실제 멤버에 접근

    # getter
    print(john.money)

    # setter
    # 양수가 기입됐을때
    john.money = 6000
    print(john.money)

    # 음수가 기입됐을 때
    john.money = - 5000
    print(john.money)

    >> setter executed
    getter executed
    0
    setter executed
    getter executed
    6000
    setter executed
    getter executed
    6000
  • Method Overriding

    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
    # 다형성(Polymophism)은 상속을 했을 때만 사용 가능
    # Method Overriding의 예

    class CarOwner:
    def __init__(self, name):
    self.name = name

    def concentrate(self):
    print(f'{self.name} is concentrating!!')

    class Car:
    def __init__(self, owner_name):
    self.car_owner = CarOwner(owner_name)

    def drive(self):
    self.car_owner.concentrate()
    print(f'{self.car_owner.name} is driving!')

    class SelfDrivingCar(Car):
    # 비슷한 기능을 자식에 맞춰서 메서드를 재정의하는것 = method overriding
    def drive(self):
    print("driving by itself")


    if __name__ == "__main__":

    car1 = Car('john')
    car2 = Car('james')
    car3 = SelfDrivingCar('yang')

    cars = []
    cars.extend((car1, car2, car3))

    # 다형성 코드
    # 결과가 다양하게 바뀔 수 있다는 의미
    for car in cars:
    car.drive()

    >> john is concentrating!!
    john is driving!
    james is concentrating!!
    james is driving!
    driving by itself
  • Inheritance(상속) : IS-A

    • 모든 멤버와 메서드가 중복이 되어야 한다.

    • 하나라도 겹치지 않으면 상속할 수 없다.

      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
      class Computer:
      def __init__(self, cpu, ram):
      self.cpu = cpu
      self.ram = ram

      def web_surf(self):
      print('surfing')

      def work(self):
      print('surfing')

      # IS-A(~은 ~의 한 종류이다.)
      # 상속의 대전제 : 모든 멤버와 메서드가 중복이 되어야 한다. 하나라도 겹치지 않으면
      # : 상속할 수 없다.
      # 재사용성(Reusability)
      class Laptop(Computer):

      # 1. 모든 멤버와 메서드가 중복된 상태에서 멤버가 추가될 때
      # 메서드 오버라이딩(다형성 구현)
      def __init__(self, cpu, ram, battery):
      super().__init__(cpu, ram)
      self.battery = battery

      # 2. 메서드가 추가될 때
      def move(self, where_to):
      print('move to {}'.format(where_to))


      if __name__ == '__main__':
      com = Computer('i5', 12)
      com.web_surf()

      print(com.cpu)

      notebook = Laptop('i7', 32, '19V_4.1am_80W')
      notebook.web_surf()
      notebook.move('office')

      print(notebook.ram)

      >> surfing
      i5
      surfing
      move to office
      32
  • Composition(통합) : HAS-A

    • 객체의 생성 시점과 소멸 시점이 같다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class CPU:
      def __init__(self, man):
      self.man = man

      class RAM:
      def __init__(self, gi):
      self.gi = gi

      # HAS-A(~이 ~을 가지고 있다. or 포함하고 있다.)
      # 객체의 멤버로서 다른 객체를 포함해서 사용한다. - composition, aggregation 둘 다

      # composition
      # 객체들의 생성 시점과 소멸 시점이 같다.(same life cycle)
      # 아래 예제에서 cpu, ram은 강하게 커플링되어있다.(strongly coupled)

      # 코드 재사용성을 위해서 관계를 구축하는 거라면
      # 상속보다는 컴포지션을 사용한다.

      class Computer:
      def __init__(self, man, gi):
      self.cpu = CPU(man)
      self.ram = RAM(gi)
  • Aggregation(집합)

    • 객체의 생성과 소멸 시점이 다르다.

      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
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      # aggregation 예

      class Gun:
      def __init__(self, kind):
      self.kind = kind

      def bangbang(self):
      print(f'{self.kind} bangbang!!!!!')

      class Police:
      def __init__(self, name):
      self.name = name
      self.gun = None

      def acquire_gun(self, gun):
      self.gun = gun

      def release_gun(self):
      gun = self.gun
      self.gun = None
      return gun

      def shoot(self):
      if not self.gun:
      print('You don\'t have a gun') # \를 이용하여 '을 문자 그대로 사용
      else:
      self.gun.bangbang()


      if __name__ == '__main__':

      pol = Police('john')
      pol2 = Police('greg')
      gun = Gun('revolver')

      pol.shoot()


      # police 객체가 gun 객체를 얻은 시점
      # 객체 생성 시점이 다르다.
      pol.acquire_gun(gun)
      gun = None # 객체의 소유권을 넘겼을 경우, None로 처리해줘야 한다.

      pol2.acquire_gun(gun)

      pol.shoot()
      pol2.shoot()

      gun = pol.release_gun()

      pol.shoot()

      del pol

      # police 객체가 사라져도 gun 객체는 사라지지 않는다.
      # 객체 소멸 시점이 다르다.

      >> You don't have a gun
      revolver bangbang!!!!!
      You don't have a gun
      You don't have a gun
  • 추상 클래스

    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
    from abc import ABCMeta, abstractmethod

    # 추상 클래스
    # 1. 인스턴스를 만들 수 없다.
    # 2. abstract method
    # pure virtual method : 몸체(body)가 없는 함수
    # 파생 클래스에서 반드시 overriding 해야 함!!(강제 조건)
    # 만약 파생 클래스에서도 overriding하지 않으면 그 파생 클래스도 추상 클래스가 된다.

    class Animal(metaclass = ABCMeta):
    @abstractmethod
    def say(self):
    print('nothing')
    pass

    class Lion(Animal):
    def say(self):
    print('어흥')

    class Duck(Animal):
    def say(self):
    print('꽥꽥')

    class Dog(Animal):
    def say(self):
    print('멍멍')

    class Deer(Animal):
    def say(self):
    print("사슴")


    if __name__ == '__main__':
    animals = []
    animals.extend((Lion(), Duck(), Deer(), Dog(), Duck()))

    for animal in animals:
    animal.say()

    >> 어흥
    꽥꽥
    사슴
    멍멍
    꽥꽥
Share