Python Object Oriented Programming

Python Object Oriented Programming


  • Object-Oriented Programming, OOP
  • 객체: 실생활에서 일종의 물건 속성(Attribute)와 행동(Action)을 가짐
  • OOP는 이러한 객체 개념을 프로그램으로 표현. 속성은 변수(variable), 행동은 함수(method)로 표현됨
  • 파이썬역시 객체 지향 프로그램 언어

class 구현하기 in Python

In [1]:
class SoccerPlayer(object):
    def __init__(self, name : str, position : str, back_number : int):
        self.name = name
        self.position= position
        self.back_number= back_number
    def change_back_number(self, new_number):
        print("선수의 등번호를 변경합니다: From %d t o%d" %\
              (self.back_number, new_number))
        self.back_number= new_number
    def __str__(self):
        return"Hello, My name is %s. I play in %s in center" % \
                (self.name, self.position)

class 선언하기

image.png

  • 변수와 Class명 함수명은 짓는 방식이 존재
  • snakecase: 띄워쓰기 부분에 '' 를추가
    :뱀처럼늘여쓰기, 파이썬함수/변수명에사용
  • CamelCase: 띄워쓰기 부분에 대문자
    :낙타의등모양, 파이썬Class명에사용

Attribute 추가하기

image.png

  • Attribute 추가는__init___, self와 함께!
  • __init__은 객체 초기화 예약 함수

파이썬에서 _의미

image.png

  • __는 특수한 예약 함수나 변수 그리고 함수명 변경(맨글링)으로 사용
    예) __main__ , __add__ , __str__ , __eq__
In [2]:
jinhyun= SoccerPlayer("Jinhyun", "MF", 10)
print(jinhyun)
Hello, My name is Jinhyun. I play in MF in center
  • print()함수로 class를 출력하게 되면 __str__함수의 반환값이 출력된다.

method 구현하기

image.png

  • self는 생성된 instance자신을 의미하게 된다.
  • method(Action) 추가는 기존 함수와 같으나,
    반드시 self를 추가해야만 class 함수로 인정됨

objects(instance) 사용하기

image.png

  • Object 이름 선언과 함께 초기값 입력 하기
In [3]:
jinhyun= SoccerPlayer("Jinhyun", "MF", 10)
print("현재선수의등번호는:", jinhyun.back_number)
jinhyun.change_back_number(5)
print("현재선수의등번호는:", jinhyun.back_number)
현재선수의등번호는: 10
선수의 등번호를 변경합니다: From 10 t o5
현재선수의등번호는: 5


구현 가능한 OOP만들기 - 노트북

구현 해야 할 것

  • Note를 정리하는 프로그램
  • 사용자는 Note에 뭔가를 적을 수 있다.
  • Note에는 Content가 있고, 내용을 제거 할 수 있다.
  • 두 개의 노트북을 합쳐 하나로 만들 수 있다.
  • Note는 Notebook에 삽입된다.
  • Notebook은 Note가 삽일 될 때 페이지를 생성하며,
    최고 300페이지까지 저장 가능하다.
  • 300 페이지가 넘으면 더 이상 노트를 삽입하지 못한다.

class scheme

image.png

Note class

In [4]:
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

NoteBook class

In [5]:
class NoteBook(object):
    def __init__(self, title):
        self.title= title
        self.page_number= 1
        self.notes= {}
    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가모두채워졌습니다.")
    def remove_note(self, page_number):
        if page_number in self.notes.keys():
            return self.notes.pop(page_number)
        else:
            print("해당페이지는존재하지않습니다")
    def get_number_of_pages(self):
        return len(self.notes.keys())
In [6]:
quote_book = NoteBook("The Quote Book")

new_note = Note()
new_note.write_content("Don't cry because it's over, smile because it happened. ― Dr. Seuss")
quote_book.add_note(new_note)

print(quote_book.get_number_of_pages())
print(quote_book.notes[1])

quote_book.add_note(Note("Hello, World"))
print(quote_book.get_number_of_pages())
my_note = quote_book.remove_note(2)
print(my_note)
1
Don't cry because it's over, smile because it happened. ― Dr. Seuss
2
Hello, World


OOP characteristics

상속(Inheritance)

In [7]:
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Korean(Person):
    pass

first_korean = Korean("Sungchul", 35)
print(first_korean.name,first_korean.age)
Sungchul 35
  • 부모클래스로 부터 속성과 Method를 물려받은 자식 클래스를 생성 하는 것
In [8]:
class Person:  # 부모 클래스 Person 선언
    def __init__(self, name, age, gender):
        self.name = name  # 속성값 지정, 해당 변수가 클래스의 attribute임을 명확히하기 위해 self를 붙임
        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, " 입니다.")


myPerson = Person("John", 34, "Male")
myEmployee = Employee("Daeho", 34, "Male", 300000, "2012/03/01")
myPerson.about_me()
myEmployee.about_me()
저의 이름은  John 이구요, 제 나이는  34 살 입니다.
저의 이름은  Daeho 이구요, 제 나이는  34 살 입니다.
제 급여는  300000 원 이구요, 제 입사일은  2012/03/01  입니다.
  • 여기서 super()라는 명령어는 부모 클래스를 불러오는 명령어이다.

다형성(Polymorphism)

  • 같은 이름 메소드의 내부 로직을 다르게작성
  • Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모클래스의 상속에서 주로 발생함
  • 중요한 OOP의 개념 그러나 너무 깊이 알 필요X image.png
In [9]:
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())
Missy: Meow!
Mr. Mistoffelees: Meow!
Lassie: Woof! Woof!

가시성(Visibility)

  • 객체의 정보를 볼 수 있는 레벨을 조절하는 것
  • 누구나 객체 안에 모든 변수를 볼 필요가 없음

    1) 객체를 사용하는 사용자가 임의로 정보 수정
    2) 필요 없는 정보에는 접근 할 필요가 없음
    3) 캡슐화 또는 정보은닉(Information Hiding)
    4) Class를 설계할 때, 클래스 간 간섭/정보공유의 최소화

Example1

  • Product객체를 Inventory객체에 추가
  • Inventory에는 오직 Product객체만 들어감
  • Inventory에 Product가 몇 개인지 확인이 필요
  • Inventory에 Product items는 직접 접근이 불가

image.png

만약 __가 없다면

In [10]:
class Product(object):
    pass

class Inventory(object):
    def __init__(self):
        self.items = []

    def add_new_item(self, product):
        if type(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)
In [11]:
my_inventory = Inventory()
my_inventory.add_new_item(Product())
my_inventory.add_new_item(Product())
my_inventory
new item added
new item added
Out[11]:
<__main__.Inventory at 0x153009e2b88>
  • 여기서 메서드를 통해서 접근한걸 볼 수 있다.
  • Product객체만 넣기 위해서 함수를 만들었다.
In [12]:
my_inventory.items.append('abc')
my_inventory.items
Out[12]:
[<__main__.Product at 0x153009e2f48>,
 <__main__.Product at 0x153009e2988>,
 'abc']
  • 이렇게 마음대로 외부에서 접근할 수 있기때문에 Product객체가 아니라 다른 자료형이 들어온걸 확인 할 수 있다.
  • 이러면 문제가 생긴다.
In [13]:
class Product(object):
    pass

class Inventory(object):
    def __init__(self):
        self.__items = []

    def add_new_item(self, product):
        if type(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())
print(my_inventory.get_number_of_items())

#print(my_inventory.__items)        # 직접 접근 불가
#my_inventory.add_new_item(object)  # 유효 타입이 아님
new item added
new item added
2
  • __을 통해서 변수를 설정하면 외부에서 접근이 불가하다.

Example2

  • 위 Example1에서 4번째 조건만 변경
  • Inventory에 Product items접근 허용
In [14]:
class Product(object):
    pass

class Inventory(object):
    def __init__(self):
        self.__items = []
    
    def add_new_item(self, product):
        if type(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)
    
    @property
    def items(self):
        return self.__items
In [15]:
my_inventory = Inventory()
my_inventory.add_new_item(Product())
my_inventory.add_new_item(Product())
new item added
new item added
In [16]:
my_inventory.items
Out[16]:
[<__main__.Product at 0x15300a22648>, <__main__.Product at 0x15300a22688>]
  • @property를 사용하면 접근 가능
In [17]:
my_inventory.items.append('abc')
my_inventory.items
Out[17]:
[<__main__.Product at 0x15300a22648>,
 <__main__.Product at 0x15300a22688>,
 'abc']
  • 하지만 이렇게 하면 또 내부의 값을 바꿀 수 있게 된다.
  • 이걸 막기 위해서는 값을 바로 리턴하는게 아니라 copy()함수를 통한 값을 반환하는게 일반적이다.


First-class objects

  • 일등함수 또는 일급 객체
  • 변수나 데이터 구조에 할당이 가능한 객체
  • 파라메터로 전달이 가능 + 리턴 값으로 사용
In [18]:
def square(x):
    return x * x
f = square
f(5)
Out[18]:
25
  • 함수를 변수로 사용
In [19]:
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]

print(formula(square,[1,2,3]))
print(formula(cube,[1,2,3]))
[1, 4, 9]
[1, 8, 27]
  • 함수를 파라메터로 사용


inner function

In [20]:
def print_msg(msg):
    def printer():
        print(msg)
    printer()
print_msg("Hello, Python")
Hello, Python
  • 함수내 또 다른 함수가 존재
In [21]:
def print_msg(msg):
    def printer():
        print(msg)
    return printer
another = print_msg("Hello, Python")
another()
Hello, Python
  • closures: inner function을 return값으로 반환
In [22]:
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")

print(h1_func())
print(p_func())
<title>This is Python Class<title>
<p>Data Academy<p>
  • 여기서 return값이 function이므로 결과를 보려면 함수 호출로 봐야한다.


decorator

In [23]:
def star(func):
    def inner(*args, **kwargs):
        print("*"* 30)
        func(*args, **kwargs)
        print("*"* 30)
    return inner
@star
def printer(msg):
    print(msg)
printer("Hello")
******************************
Hello
******************************
  • 복잡한 클로져 함수를 간단하게 만들어 준다.
In [24]:
def star(func):
    def inner(*args, **kwargs):
        print(args[1]* 30)
        func(*args, **kwargs)
        print(args[1]* 30)
    return inner
@star
def printer(msg,mark):
    print(msg)
printer("Hello",'&')
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
Hello
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
In [25]:
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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
In [26]:
def generate_power(exponent):
    def wrapper(f):
        def inner(*args):
            result = f(*args)
            return exponent**result
        return inner
    return wrapper

@generate_power(2)
def raise_two(n):
    return n**2

print(raise_two(7))
562949953421312

'AI > 이론' 카테고리의 다른 글

Numpy part II  (0) 2021.01.25
Numpy part I  (0) 2021.01.25
Python data handling  (0) 2021.01.22
File & Exception & Log Handling  (0) 2021.01.22
Module and Project  (0) 2021.01.21
pythonic code  (0) 2021.01.20
python data structure  (0) 2021.01.20
Variable & List  (0) 2021.01.19

+ Recent posts