[BoostCamp AI Tech / Day 2] Python Object Oriented Programming

newbie·2021년 8월 3일
0

Index

  • 클래스와 객체(객체 지향 언어의 이해)
  • 객체지향 프로그래밍 개요
  • Object in Python
  • 객체 지향 언어의 특징
    • Inheritance
    • Polymorphism
    • Visibility
  • decorator 이해

클래스와 객체(객체 지향 언어의 이해)

  • ex) 수강신청 프로그램
    • 1) 수강신청이 시작부터 끝까지 순서대로 작성
    • 2) 수강신청 관련 주체들(교수,학생,관리자)의 행동(수강신청, 과목입력)과 데이터(수강과목, 강의 과목, 학점)들을 중심으로 프로그램 작성 후 연결
  • 두 가지 모두 가능하나, 가독성이 높으며 재사용이 편리한 2) 방식이 주류
  • 이러한 기법을 객체 지향 프로그램(OOP) 라고 함

객체지향 프로그래밍 개요

  • Object-Oriented Programming,OOP
  • 객체 : 실생활에서 일종의 물건으로, 속성(attribute)와 행동(action)을 가짐
  • OOP는 이러한 객체 개념을 프로그램으로 표현, 속성은 변수(variable), 행동은 함수(method)로 표현
  • 파이썬 역시 객체 지향 프로그램
  • OOP는 설계도에 해당하는 **클래스(Class)와 실제 구현체인 인스턴스 혹은 객체(instance, object)로 나눔

Object in Python

  • 축구선수 정보를 Class로 구현
class SoccerPlayer(object):
#클래스 예약어 + Class명(상속받는 객체명)
# Class명 : CamelCase로 적기
  #CamelCase : 띄어쓰기 부분에 대문자, Class명
  #snake_case : 띄어쓰기 부분에 "("추가, 함수명
# 상속받는 객체가 없을 경우 자동 상속되어 생략해도 무방함 => Class명(), Class명
    def __init__(self, name : str, position : str, back_number : int): #__ini__ : 객체 초기화 예약 함수로, class를 실행 시 초기화를 하며,
                                                                       #self은 외부에선 해당 class를 선언한 instance명이고 내부에서는 self로 불림
        self.name = name                             #객체 자신인 self와 객체에 필수로 할당할 name, positon, back_number와 같은 atrribute를 설정
        self.position = position                     #namedtuple과 비슷함
        self.back_number = back_number               # attribute 지정 시 str, int와 같이 설명을 같이 넣어주면 다른 사용자가 이용하기 용이함
        
    
    def change_back_number(self, new_number): #특정 atrribute에 행동을 취하는 함수를 생성(method)
        # f-string
        print(f"선수의 등번호를 변경합니다 : From {self.back_number} to {new_number}")
        # % operator string 
#         print("선수의 등번호를 변경합니다 : From %d to %d" %(self, back_number, new_number))
        # format-string
#         print("선수의 등번호를 변경합니다 : From {} to {}".format(self.back_number, new_number))
#         print("선수의 등번호를 변경합니다 : From {aa} to {bb}".format(aa = self.back_number, bb = new_number))
        #가장 나은 건 가시성이 좋으며 속도가 가장 빠른 f-string(가장 최근에 나옴)
        self.back_number = new_number
    
    
    def __str__(self): #print와 동일한 역할
        return "Hello, My name is {name}. I Play in {position}".format(name = self.name, position = self.position)
    
    
    def __add__(self, other): # 더하기 연산 or concat
         return self.back_number + other
aa = SoccerPlayer("ko","fw",21)
bb = SoccerPlayer("ko","fw",21)
#여기서 aa,bb는 SoocerPlayer라는 클래스에서 파생된 객체
# aa,bb의 argument는 같으나 다른 인스턴스
  • [알아두면 상식]
    • __는 특수 예약 함수나 변수 그리고 함수명 변경(맨글링)으로 사용
    • 예) 와 사이에 main, add, _str, eq 등이 들어와 예약함수로 사용됨
    • str == print문을 객체에 적용하면 출력해주는 함수
      -ex) tmp = SoccerPlayer("jin","MF",21)로, tmp라는 객체가 있을 때 print(tmp)를 하면 str이 들어가는 예약함수가 실행됨

객체 지향 언어의 특징

  • Inheritance, Polymorphism, Visibility

    • 상속(Inheritance)

      • 부모 클래스로부터 속성과 Method를 물려받은 자식 클래스를 생성하는 것
      class Person:
          def __init__(self,name,age):
              self.name = name
              slef.age = age
       class Korean(Person):
           pass
      • Korean이란 Class에는 아무런 내용이 없더라도 Person을 상속받아 동일한 method와 attribute를 지님
    • 다형성(Polymorphism)

      • 같은 이름의 메소드지만 내부 로직을 다르게 작성
      • Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모클래스의 상속에서 주로 발생하게 됌
      • ex), 부모 클래스와 자식 클래스 모두 printer()라는 함수가 있지만, 부모 클래스는 argument값만 출력해주고 자식 클래스는 특정 foramt에 맞춰 출력
      class tmp1:
          def __init__(self,string):
              self.string = string
          def printer(self):
              return string
      class tmp2(tmp1):
          def printer(self):
             return f"입력된 문자열은 {self.string} 입니다"
      tmp1("나는").printer() # '나는'
      tmp2("나는").printer() # '입력된 문자열은 나는 입니다'
    • Visibility

      • 객체의 정보를 볼 수 있는 레벨을 조절하는 것

      • 누구나 객체 안에 모든 변수를 볼 필요는 없음
        1)객체를 사용하는 사용자에게 실수로 정보 수정하는 것을 방지
        2) 필요 없는 정보에는 접근을 불가하게 만듬
        3) 실제 판매되는 제품의 소스 보호

      • Encapsulation

        • 캡슐화 또는 정보 은닉(information Hiding)
        • Class 설계 시클래스간 간섭/정보 공유 최소화
        • ex) 심판 클래스가 선수 클래스의 가족까지 알 필요 없음
        • 인터페이스만 알아서 쓰게 함
        class Product:
            pass
        
        class Inventory:
            def __init__(self):
                self.items = []
                self.test = "abc"
            
            def add_new_item(self,product):
                if type(product) == Product:
                    self.items.append(product)
                    print(f"new item added({type(product)})")
                else:
                    raise ValueError("Invalied Item")
                
            def get_number_of_items(self):
                return len(self.items)
                
        my_inventory = Inventory()
        my_inventory.add_new_item(Product())
        # class 내부 변수에 특정한 조건이 없을 경우 외부에서 접근 가능
        # 따라서 my_inventory.items에다가 append() 혹은 print() 등을 통해 추가하거나 보는 등의 행동을 제한없이 가능
        # 제한하고자 할 경우, attribute 선언 시 변수명에 __ 추가해주면됨(=네임 맹글링 : https://tibetsandfox.tistory.com/21)
        # 네임 맹글링을 사용하면 private화 할 수 있지만 완벽한 private은 아님
        # 조금 더 찾기 어렵게 만들 뿐
        # 단, attribute나 method의 다형성을 억제할 순 있음
        
        class Inventory:
            def __init__(self):
                self.__items = []
                self.test = "abc"
        
            def add_new_item(self,product):
                if type(product) == Product:
                    self.__items.append(product)
                    print(f"new item added({type(product)})")
                else:
                    raise ValueError("Invalied Item")
        
            def get_number_of_items(self):
                return len(self.__items)
        
        my_inventory = Inventory()
        				my_inventory.items.append("Abc")
        # >> AttributeError: 'Inventory' object has no attribute 'items'
        my_inventory.items
        # >> AttributeError: 'Inventory' object has no attribute 'items'
        
        #단, dir(instance명) 으로 class 내부 변수의 주소를 찾으면 접근 가능
        dir(my_inventory) => '_Inventory__items' #items 위치
      • 반대로 정보 은닉을 했지만 items에 접근 허용해줄 수 있음

      • @property decorator 사용

      class Inventory:
          def __init__(self):
              self.__items = []
              self.test = "abc"
      
           @property # property decorator 숨겨진 변수를 반환하게 해줌
           # property 데코를 통해 getter의 역할 + 함수를 변수명처럼 사용하게 만들어줌
           # https://dojang.io/mod/page/view.php?id=2476
           # property 데코는 getter의 역할(반대로 setter는 method명.setter)
           def items(self):
               return self.__items
      
           def add_new_item(self,product):
               if type(product) == Product:
                   self.__items.append(product)
                   print(f"new item added({type(product)})")
               else:
                   raise ValueError("Invalied Item")
      
           def get_number_of_items(self):
               return len(self.__items)
      my_inventory = Inventory() 
      my_inventory.items # 함수로 만들었지만 변수명처럼 사용 가능

decorator 이해

  • first-class object

    • 일등함수 혹은 일등 객체
    • 변수나 데이터 구조에 할당이 가능한 객체(ex) map(f,value)
    • 파라미터로 전달이 가능 + 리턴 값으로 사용
    def square(n):
        return x**2
    
    def cube(n):
        return x**3
        
    method = [square, cube]
    arg_list = [(0,2),(1,2)]
    [method[i](n) for i,n in arg_list] =>[4,8]
  • inner function

    • 함수 내에 또 다른 함수가 존재
    • closures
      • inner function 자체가 return값으로 반환
    def print_msg(msg):
        def printer():
            return msg
        return printer()
    print_msg("hello")
  • decorator

    • 복잡한 closure 함수를 간단하게 구현가능
    def star(func):
        def inner(*args, **kwargs):
            print("*"*5)
            func(*args,kwargs)
            print("*"*5)
        return inner
    
    @star # @star 시 @star 이후의 printer 함수가 star 함수의 agument로 됨
          # star(func = printer)
    def printer(msg):
        print(msg)
    printer("hello")
profile
DL, NLP Engineer to be....

0개의 댓글