boostcamp AITech

[4일차] OOP 특징들과 decorate / 모듈과 패키지

라이크나우 2021. 1. 21. 23:46

이 쯤되면 내일 수업은 어떨지 설렐 지경..까진 아니고 하튼 기대된다.

오늘도 항상 긴가민가하며 쓰던 것 + 남의 코드 읽으면서 이해 안가던 부분들을 싹 알려주셨다.

 

클래스와 객체의 개념에 대한 설명은 스킵하고,

 

오늘은 강의에 나온 실제 OOP코드를 살펴봐야겠다.

 

OOP with Python

선언 1

class NoteBook(object):
    def __init__(self, title):
        self.title=title
        self.page_number=1
        self.notes={}
        

우선 첫 줄은 class이름! CamelCase로 이름지어야한다 그냥 규칙~

객체는 행동과 데이터를 중심으로 돌아가잖아?

__init__은 객체 초기화 예약 함수로, 여기서 attribute(행동과 데이터 중 데이터)를 선언해주면 된다.

 

객체를 생성할 때 파라미터로 넣어줄 친구들을 적어주고 self.변수와 연결해준다.

그 외에도 초기화할 변수들이 있다면 여기서 초기화!

 

선언2

    def add_note(self, note, page = 0):
        if self.page_number < 300:
            if page==0:
                self.notes[self.page_number] = note
                self.page_number += 1
            else:
                self.notes = {page : note}
                self.page_number += 1
                
        else:
            print("page가 모두 채워져있습니다")

(NoteBook 선언하는 위 함수랑 이어지는 함수이다)

객체에서 행동 즉 함수를 선언하는 방법은 위와 같다.

page_number는 위에서 선언했으니 객체 내에 있을테다. 그러니 self로 불러온다.

다른 요소들도 마찬가지로 객체 내부의 변수에 적절하게 할당해준다.

 

**기존의 함수 선언 방법과 같으나 반드시 self를 추가해야만 class 함수로 인정된다!**

객체 사용하기(instance 만들기)

그냥 써보고싶어서 써봤는데 담부턴 걍 타이핑 쳐야겠다 ㅋㅋ...

하튼 __init__의 인터페이스에 맞춰서 인스턴스를 생성해주면 된다!

맨글링

class Note(object):
    def __init__(self, content = None):
        self.content = content
        
    def write_content(self, content):
        self.content = content
        
    def remove_all(self):
        self.content = ""
        
    def __add__(self, other):
        return self.content + other.content
        
    def __str__(self):
        return self.content

 

특수한 예약 함수나 변수 그리고 함수명 변경을 할 때 __ 맨글링을 사용한다고한다.

원래 print(instance name)을 하면 주소가 나오는데 __str__을 수정해주면 그에 맞춰서 출력해준다. 위의 경우 content를 출력해주겠지!

__add__도 마찬가지인데, 

이렇게 + 기호를 이용해서 합칠 수 있다! (연산자 오버로딩)

우선은 이렇게 가볍게만 다루자 ~

 

 

OPP characteristics

  • Inheritance
  • Polymorphism
  • Visibility

각각 상속 다형성 가시성이다.

아 배고파...

 

Inheritance

이 페이지의 맨 첫번째 코드를 보면 클래스를 선언할 때 파라미터로 Object를 넣어준걸 볼 수 있다.

저 자리는 비워두면 자동으로 Object를 상속받는다. 

만일 다른 클래스를 적어둔다면 그 클래스를 상속받겠지?

class Employee(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," 입니다.")
        
    def save(self):
    	print("돈은 다 써버려야 합니다.")

 

👆위 코드를 보자. Person이라는 Class를 상속받았다.

super()은 부모를 가리킨다. 부모의 __init__에 있는 이름 나이 성별을 가져오라는거다.

Employee는 새로운 두 파라미터를 추가로 받는걸 확인할 수 있다.

 

about_me코드를 보면 super()를 이용해 부모의 함수를 가져올 수 있단걸 볼 수 있다.

 

Polymorphism

같은 이름의 메소드의 내부 로직을 다르게 작성하는 것이다.

 

만일 save란 함수가 Person 클래스 내에서 "저축을 합시다"를 프린트하도록 선언되어있다고 하자.

employee로 만든 인스턴스로 save를 실행하면 뭐가 출력될까?

바로 "돈은 다 써버려야 합니다."가 출력된다.

부모의 함수를 오버라이딩(overriding)했기 때문이다!

 

각자 class가 가진 서로 다른 역할의 메소드가 알아서 불러진다!

Employee로 만든 인스턴스에서 save를 부르면 "돈은 다 써버려야 합니다"를 출력하겠지만

다른 class를 만들어서 내부에서 save 메소드에 "주식을 합시다"를 출력하도록 했다면 해당 인스턴스는 주식을 하라고 할 것 이다.

 

Visibility

객체의 정보를 은닉하는 것이다.

누구나 객체 안의 모든 변수를 볼 필요도 이유도 없겠지?

Encapsulation 즉 캡슐화를 이용해 구현한다.

인터페이스만 알면 내부를 몰라도 잘만 쓸 수 있다~

 

파이썬에서는 __를 붙혀서 private 변수 선언을 하는가보다.

(__를 붙히는걸 보니 맨글링인데, 자세한건 나중에 알아도 된다고 하셨다 ! ㅎ)

이제 외부에서 직접 item에 접근할 수 없다.

비교를 위해 visible_items도 만들어봤다.

 

In [42]를 보면 item들이 잘 추가되는걸 볼 수 있다.

 

하지만 직접 items에 접근하려고 하면 없는 속성이라고 나온다!

visible_items는 물론 접근이 가능하다!

 

Decorate

오늘의 하이라이트 데코레이트 (라임)

를 이해하기 전에 알아둬야할 개념들이 있었으니~

 

First-class objects

파이썬의 함수는 일급함수란다.

함수를 파라미터로 전달이 가능하고 리턴값으로도 사용이 가능하면 일급함수라고 한단다.

map(int,input().split()) <-  이 친구만 봐도 원래는 int()로 써야할 것을 변수인양 int로 사용하고 있다. 

 

Inner funcion

함수 안의 함수다

def print_msg(msg):
    def printer():
        print(msg)
    printer()
    
print_msg("Hello")

이렇게 생겼다. 이런걸 왜 하나 했더니 가독성도 있고 다른 이유 하나는 closure

오마이걸 closer 노래는 좋기만 한데,,이 클로져는 무슨일이냐....

 

closure

(velog.io/@inyong_pang/Python-Nested-Function-2wk42jt94r 참고)

Closure란, 사전적의미로는 '폐쇄'라는 뜻을 가진다.
파이썬에서 사용하는 Closure는 어떤 것을 외부로 부터 격리하여 사용한다는 느낌이 있다.
즉, 중첩함수의 부모함수가 자신의 내부에 함수나 변수의 정보를 가두어 사용하는 것을 Closure라고 하는 것이다.

 

 inner function을 return 값으로 반환함으로써 closure를 구현한다!

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")

👆closure의 예시이다.

print(h1_func)를 하면 출력 안된다

print(h1_func())를 하면 문장이 출력된다! 함수를 부르는거라 ()를 꼭 해줘야한다!   <-피어세션에서 질문해서 얻은 답변 ><

 

 

위의 블로그에 있는 코드가 이해하기 쉬워서 가지고 왔다. 작성자님 감사합니다.

def generate_power(base_number):

    def nth_power(power): 
        return base_number ** power 

    return nth_power
 
calculate_power_of_two = generate_power(2) #2는 base_number로
calculate_power_of_two(7)                  #7은 power로 (중첩된 안쪽 함수의 인자로)

calculate_power_of_seven = generate_power(7)
calculate_power_of_seven(3)

👆출력은 각각 128과 343이다 (2**7과 7**3)

 

Decorator

아니 저 블로그 뭐지.

사진 그대로 가져오고 싶다. 이해 짱잘됨진짜.

우선 decorator는 복잡한 클로져 함수를 간단하게 사용하게 해준다.

 

def star(func):				#Line#1
    def inner(*args, **kwargs):		#Line#2
        print("*" * 30)			#Line#3
        func(*args, **kwargs)		#Line#4
        print("*" * 30)			#Line#5
    return inner			#Line#6

@star
def printer(msg):
    print(msg)
printer("Hello")

 

👆우선 출력은 이렇게 된다.

******************************

Hello

******************************

  1. star함수를 부른다 printer를 파라미터로 받는다.
  2. 6번줄에서 inner 함수가 실행된다
  3. 3번줄 별 출력
  4. 4번줄 파라미터로 받은 printer함수 실행 -> Hello 출력
  5. 5번줄 별 출력

그러니까 함수를 데코레이터의 파라미터로 넘겨주면서 inner함수를 제대로 실행해준다.

printer가 실행되기 전에 필수적으로 실행해야하는 함수가 있다면(지금은 star) 사용해주면 된다!

 

👆위 캡처는 데코레이터가 어떤 식으로 작동하는지 그려보았다.

여기서는 exponenet를 항상 설정해주고 raise_two를 실행해야하니 데코레이터로 적어준걸 볼 수 있다.

 

이해는 된 것 같은데 직접 써보면 좀 버벅일 것 같다.

 

제너레이터를 쓰지 않고 저 코드를 풀어써보려면 이렇게 해야한다. (질문이었는데 조교님이 답변해주셨다!)

def raise_two(n):
    return n**2

def inner(*args, exponent=2):
    result = raise_two(*args)
    return exponent ** result

inner(7, exponent=2)

👆제너레이터 없이 inner 를 쓸 수 있는 방법이 없다 두둥 그래서 이렇게 함수로 빼서 풀어써야한다.

 

이번엔 남이 만든 프로그램을 잘 써보자~time~

Module

작은 프로그램의 조각들(   .py 파일들)

프로그램을 모듈화 시키면 다른 프로그램이 사용하기 쉽다 (api가 모듈이라는거지?)

import 파일명   << 이렇게 사용하면 된다.

  • import numpy as np                  :                  모듈명을 별칭으로 사용해서 내부 클래스/함수는 np.array 이런식으로 쓴다. (추천)
  • from numpy import array          :                  특정 함수 또는 클래스만 호출한다
  • from numpy import *                :                  전체 다 호출한다. numpy.함수명 이런식으로 안쓰고 함수명만 써도 된다

빌트인 모듈도 많으니 알아서 검색해서 잘 사용할 것*^^*

 

패키지

모듈을 모아놓은 단위이다 다들 폴더로 연결된다.

__init__, __main__ 등 키워드 파일명이 사용된다

이제야 하나의 프로그램이다!

 

원래는 폴더에 __init__.py 가 있어야 현재 폴더가 패키지임을 알려줬었는데 이제는 안써도 된다(그치만 다들 쓴대)

 

 

그리고 컴터에 뭐 깔기 두렵고 막 그러면 가상환경 만들어서 하라는 꿀팁과 함께 오늘의 강의 끝~ 내 하루도 끝~

conda create -n my_project python=3.8

이런식으로 호출하면된다.

 

 

여기부턴 피어세션 정리!

 

isdigit isnumeric isdemical의 차이를 설명해준 블로그이다.

soooprmx.com/archives/10159

 

파이썬의 숫자판별함수 · Wireframe

파이썬에서 주어진 문자열이 숫자로 되어 있는지를 검사하는데에는 흔히 문자열의 메소드인 isdigit()을 사용한다. 그런데 파이썬의 문자열 타입을 조사해보면 같은 기능을 하는 거 같은 메소드

soooprmx.com

demical < digit < numeric 순으로 덜 예민하다.

 

또, 제너레이터가 한번 돌고나면 끝난다는 점 / list의 크기 차이 / tuple(,)일 때 쉼표의 기능에 대해 한번 씩 더 생각해볼 수 있었다. 자세한건 3일차 복습노트에 해당되는 내용을 추가할 것이니 미래의 나는 보물찾기 하듯이 찾아보도록 *^^*