Posts [부스트캠프 AI Tech / Day4] 파이썬 OOP
Post
Cancel

[부스트캠프 AI Tech / Day4] 파이썬 OOP

[DAY 4] Object-Oriented Programming (OOP)


객체지향언어의 특징

  • Inheritance(상속)
  • Polymorphism(다형성)
  • Visibility(Hidden class, 가시성)

Inheritance (상속)

  • 부모클래스로 부터 속성과 Method를 물려받은 자식 클래스를 생성 하는 것
  • super: 자신의 부모 클래스
  • self: 자기자신(클래스,객체)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    class Person(object):
      def __init__(self, name, age):
        self.name = name
        self.age = age
    
      class Korean(Person): # 상속을 받았기 때문에, Person 내부의 속성을 사용가능
        pass
    
      first_korean = Korean("Sungchul", 35)
      print(first_korean.name)
    
  • inheritance example

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    # class Person: # 이렇게 써도 됨
    # 따로 상속받지 않은 클래스는 자동으로 Object class를 상속받음
    class Person(object): # 부모 클래스 Person 선언
      def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
      def about_me(self): # Method 선언
        print("저의 이름은 ", self.name, "이구요, 제 나이는 ", str(self.age), "살 입니다.")
    
    class Employee(Person): # 부모 클래스 Person으로 부터 상속
      def __init__(self, name, age, gender, salary, hire_date):
        super().__init__(name, age, gender) # 부모객체 사용
        self.salary = salary
        self.hire_date = hire_date # 속성값 추가
      def do_work(self): # 새로운 메서드 추가
        print("열심히 일을 합니다.")
      def about_me(self): # 부모 클래스 함수 재정의
        super().about_me() # 부모 클래스 함수 사용
        print("제 급여는 ", self.salary, "원 이구요, 제 입사일은 ", self.hire_date,
      " 입니다.")
    

Polymorphism(다형성)

  • 같은 이름 메소드의 내부 로직을 다르게 작성
  • Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모클래스의 상속에서 주로 발생함
  • 같은 일을 하지만, 세부 구현이 다른 경우
  • 같은 이름, 다른 역할
  • 같은 이름을 쓰되, 각각의 목적에 따라 다르게 구현하는 것

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    class Animal:
      def __init__(self, name): # Constructor of the class
        self.name = name
      def talk(self): # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")
    
    class Cat(Animal):
      def talk(self):
        return 'Meow!'
    
    class Dog(Animal):
      def talk(self):
        return 'Woof! Woof!'
    
    animals = [Cat('Missy'), Cat('Mr. Mistoffelees'), Dog('Lassie')]
    for animal in animals:
      print(animal.name + ': ' + animal.talk())
    

Visibility(가시성)

  • 객체의 정보를 볼 수 있는 레벨을 조절하는 것
  • 누구나 객체 안에 모든 변수를 볼 필요가 없음
    • 1) 객체를 사용하는 사용자가 임의로 정보 수정
    • 2) 필요 없는 정보에는 접근 할 필요가 없음
    • 3) 만약 제품으로 판매한다면? 소스의 보호
  • Encapsulation (비슷한 용어)
    • 캡슐화 또는 정보 은닉(Information Hiding)
    • Class를 설계할 때, 클래스 간 간섭/정보공유의 최소화
    • 심판 클래스가 축구선수 클래스 가족 정보를 알아야 하나?
    • 캡슐을 던지듯, 인터페이스만 알아서 써야함
    • 사용법만 알고있다면 내 코드를 누구나 마음대로 쓸 수 있음
  • Visibility Example 1
    • Product 객체를 Inventory 객체에 추가
    • Inventory에는 오직 Product 객체만 들어감
    • Inventory에 Product가 몇 개인지 확인이 필요
    • Inventory에 Product items는 직접 접근이 불가
    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
    
      class Product(object):
        pass
    
      class Inventory(object):
        def __init__(self):
          self.__items = [] # private 변수로 선언, 타 객체가 변수에 접근하지 못함
        def add_new_item(self, product):
          if type(product) == Product: # Product 클래스 타입인지 확인
            self.__items.append(product)  # 리스트에 추가
            print("new item added")
          else:
            raise ValueError("Invalid Item")
        def get_number_of_items(self):
          return len(self.__items)
    
      my_inventory = Inventory()
      my_inventory.add_new_item(Product())
      my_inventory.add_new_item(Product())  # 가능
    
      # self.items = []
      my_inventory.items.append("abc")      # 가능
      data = my_inventory.items             # 가능
    
      # self.__items = []
      # 직접 접근, 수정 불가능
      my_inventory.__items.append("abc")      # 불가능
      data = my_inventory.__items             # 불가능
    
      print(my_inventory.get_number_of_items())
      print(my_inventory.__items)
      my_inventory.add_new_item(object)
    
  • Visibility Example 2
    • Product 객체를 Inventory 객체에 추가
    • Inventory에는 오직 Product 객체만 들어감
    • Inventory에 Product가 몇 개인지 확인이 필요
    • Inventory에 Product items 접근 허용
    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
    
    class Inventory(object):
      def __init__(self):
        self.__items = []
      @property # property decorator: 숨겨진 변수를 반환하게 해줌
      def items(self):
        return self.__items
      # 함수명을 변수명으로 쓸 수 있게 해줌
      # 데이터 복사 없이, 객체 내부 변수를 바로 반환하면, 내부 변수까지 수정하는 경우 있을 수 있음
      # 데이터를 복사하여 리턴하는 경우가 많음
    
      my_inventory = Inventory()
      my_inventory.add_new_item(Product())
      my_inventory.add_new_item(Product())
      print(my_inventory.get_number_of_items())
    
      items = my_inventory.__items      # 불가능
      items = my_inventory.items        # 가능(내부 접근 가능, 내부에서 접근하여(함수로) 반환 가능)
      my_inventory.items.append("abc")  # 가능
      # 값을 바꿔버릴 수 있기 때문에 아래와 같이 데이터를 복사한 후, 리턴하게 됨
      """
      @property
      def items(self):
        ret = self.__items[:]
        return ret
      """
    
      items.append(Product())
      print(my_inventory.get_number_of_items())
    



Decorate

  • first-class objects(1급 함수)
  • inner function
  • decorator

First-class objects

  • 일등함수 또는 일급객체
  • 변수나 데이터 구조에 할당이 가능한 객체(객체를 변수에 할당)
  • 파라미터로 전달이 가능 + 리턴값으로 사용 가능
  • 파이썬의 함수는 일급함수
  • ex) map(f, ex)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    def square(x):
      return x * x
    f = square # 함수를 변수로 사용
    f(5)
    
    ##################################
    def square(x):
      return x * x
    def cube(x):
      return x*x*x
    def formula(method, argument_list): # 함수를 파라메터로 사용
      return [method(value) for value in argument_list]
    
    # 두개의 formula를 만드는 것보다, 하나의 formula가 다른 메소드를 활용할 수 있게 함
    # 장점: 구조화 체계를 만들어 줌
    # 단점: 이해하기 어려움
    # formula(square, ~)
    # formula(cube, ~)
    

Inner function

  • 함수 내에 또 다른 함수가 존재

    1
    2
    3
    4
    5
    6
    
    def print_msg(msg):
      def printer():
        print(msg)
      printer()
    
    print_msg("Hello, Python")
    
  • closures : inner function을 return값으로 반환

    1
    2
    3
    4
    5
    6
    
    def print_msg(msg):
      def printer():
       print(msg)
      return printer  # inner class return # 함수를 리턴
    another = print_msg("Hello, Python") # another은 함수!
    another()
    
    • 예시(closure example)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
        def tag_func(tag, text):
          text = text
          tag = tag
          def inner_func():
            return '<{0}>{1}<{0}>'.format(tag, text)
          return inner_func
      
        h1_func = tag_func('title', "This is Python Class")
        p_func = tag_func('p', "Data Academy")
        # 비슷한 목적을 가지는 다양한 함수 생성 가능
      

Decorator function

  • 복잡한 클로져 함수를 간단하게

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    def star(func):
      def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
      return inner
    
    @star               # star라는 이름의 func에
    def printer(msg):   # 인자로 들어감
      print(msg)
    
    printer("Hello")
    """
    ******************************
    Hello
    ******************************
    """
    printer("Hello", "&")
    """
    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
    Hello
    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
    """
    
    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
    
    def star(func):
      def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
      return inner
    
    def percent(func):
      def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
      return inner
    
    @star
    @percent
    def printer(msg):
      print(msg)
    printer("Hello")
    
    """
    ******************************
    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
    Hello
    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
    ******************************
    """
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    def generate_power(exponent): # 2
      def wrapper(f): # f = raise_two
        def inner(*args): # n
          result = f(*args)
          print(result) # 49
          return exponent**result
        return inner
      return wrapper
    
    @generate_power(2)
    def raise_two(n):
      return n**2
    
    print(raise_two(7))
    # 562949953421312 (= 2**49)
    
This post is licensed under CC BY 4.0 by the author.