728x90

Python Object Oriented Programming


[생각해보기]

Q. 수강신청 프로그램을 작성하는 방법

① 수강신청을 시작부터 끝까지 순서대로 작성

② 수강신청 관련 주체들(교수, 학생, 관리자) 행동(수강신청, 과목 입력) 데이터(수강과목, 강의 과목)들을 중심으로 프로그램 작성 후 연결

 

→ ② 와 같은 기법이 객체 지향 프로그램

 

 

객체지향 프로그래밍 개요

  • Object-Oriented-Programming (OPP)
  • 객체 : 속성(Attribute) + 행동(Action)
  • OOP는 이러한 객체 개념을 프로그램으로 표현
  • 속성 - 변수(Variable) / 행동 - 함수(method)로 표현
  • 파이썬 역시 OOP언어
  • 예시 : 인공지능 축구 프로그램 작성
    • 객체 종류 : 팀, 선수, 심판, 공
    • Action
      • 선수 : 공을 차다, 패스하다
      • 심판 : 휘슬을 불다, 경고를 주다
    • Attribute
      • 선수 : 선수 이름, 포지션, 소속팀
      • 팀 : 팀 이름, 팀 연고지, 팀소속 선수
  • OOP 구성
    • 클래스(class) / 인스턴스(instance -> 실제 구현체)

 

 

Objects in Python

 

1. class

  • 축구 선수 정보를 Class로 구현하기
class SoccerPlayer(object):
	def __init__(self, name, position, back_number):
    	self.name = name
        self.position = position
        self.back_number = back_number
    
    def change_back_number(self, new_number):
    	print('선수의 등번호를 변경합니다 : From %d to %d' % (self.back_number, new_number):
        self.back_number = new_number
  • class  선언 시 object 자동 상속(python3 기준)

class 헤더 구성

  • Python naming rule
    • 변수/함수명 - snake_case
    • 클래스명 - CamelCase

 

2. Attribute

  • Attribute 추가 ( __init__, self )
    • __init__ : 객체 초기화 예약 함수

 

__init__

  • 파이썬에서 __ 의 의미 : 특수한 예약 함수 / 변수, 함수명 변경(맨글링)으로 사용
  • 예시 : __main__, __add__, __str__, __eq__ 등
class SoccerPlayer(object):
	def __str__(self):
    	return "Hello, My name is %s. I play in %s in center." % \
        (self.name, self.position)
 
james = SoccerPlayer('james', 'MF', 10)
print(james)    # Hello, My name is james. I play in MF in center.

 

3. Method

  • 반드시 self를 추가해야만 class 함수로 인정됨

method

 

4. Objects(instance)

인스턴스 사용법 : 인스턴스 이름 선언과 함께 초기값 입력

인스턴스 사용법

 

5. class 구현 및 사용

class SoccerPlayer(object):
	def __init__(self, name, position, back_number):
    	self.name = name
        self.position = position
        self.back_number = back_number
    
    def change_back_number(self, new_number):
    	print('선수의 등번호를 변경합니다 : From %d to %d' % (self.back_number, new_number))
        self.back_number = new_number

james = SoccerPlayer('james', 'MF', 10)
print('현재 선수의 등번호는 :', james.back_number)    # 현재 선수의 등번호는 10
james.change_back_number(5)
print('현재 선수의 등번호는 :', james.back_number)    # 현재 선수의 등번호는 5

 

 

OOP Implementation Example

 

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

  • Note 정리 프로그램
  • 사용자는 Note에 뭔가 적을 수 있음
  • Note에는 Content가 있고, 내용 제거 가능함
  • 두 개의 노트북을 합쳐 하나로 만들 수 있음
  • Note는 Notebook에 삽입됨
  • Notebook은 Note가 삽입 될 때 페이지 생성, 최대 300페이지까지 저장 가능
  • 300페이지 초과로는 노트 삽입 불가

 

class scheme

class scheme

 

 

1. Note Class

'''
content :
- write_content
- remove_all
'''
class Note():
    def __init__(self, content=None):
    	self.content = content
    
    def wirte_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

 

2. NoteBook class

'''
- title, page_number, notes
- add_note
- remove_note
- get_number_of_pages
'''

class NoteBook():
	def __init__(self, title):
    	self.title = title
        self.page_number = 1
        self.notes = {}
    
    def add_note(self, note, page = -1):
    	if self.page_number < 301:
        	if page == -1:
            	self.notes[self.page_number] = note
                self.pages_number += 1
            else:
            	self.notes[page] = note
        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())

 

OOP 언어 특징

  • Inheritance 상속성
  • Polymorphism 다형성
  • Visibility 가시성

 

Inheritance 상속

  • 부모 클래스로부터 속성과 method를 물려받은 자식 클래스를 생성하는 것
  • 예시
class Person():    # 부모 클래스 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) 특성 → 파이썬에서는 같은 부모 클래스의 상속에서 주로 발생
  • 예시
class Animal():
    def __init__(self, name):     # contructor 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 가시성

  • 객체의 정보를 볼 수 있는 레벨 조절하는 것
  • 누구나 객체 안의 모든 변수를 볼 필요 없음
    • 객체를 사용하는 사용사가 임의로 정보 수정
    • 필요 없는 정보에는 접근 할 필요가 없음
    • 만약 제품으로 판매한다면 → 소스의 보호 필요
  • Encapsulation
    • 캡슐화 or 정보 은닉(Information Hiding)
    • Class 설계 시, 클래스 간 간섭 / 정보공유의 최소화
    • 알리면 안되거나 알 필요 없는 정보 숨기는 역할
  • 예시 1
    • Product 객체를 Inventory 객체에 추가
    • Inventory에는 오직 Product 객체만 들어감
    • Inventory에 Product가 몇 개인지 확인 필요
    • Inventory Product items는 직접 접근 불가
class Product():
    pass

class Inventory():
    def __init__(self):
        self.__items = []    # Private 변수로 선언. 타 객체가 접근 못함
    
    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())
'''
실행결과:
new item added
new item added
2
'''
print(my_inventory.__items)

'''
실행결과:
AttributeError: 'Inventory' object has no attribute '__items'
'''
my_inventory.add_new_item(object)

'''
실행결과:
ValueError: Invalid Item
'''

 

  • 예시 2 : property decorator 사용해 숨겨진 변수 반환 가능
class Inventory():
    def __init__(self):
        self.__items = []
    
    # 추가된 부분 : property decorator 숨겨진 변수를 반환하게 해줌
    @property
    def items(self):
        return 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)

 

 

decorate

class Student:
    def __init__(self, name, marks):
        self.name = name
        self.marks = marks
        # self.gotmarks = self.name + ' obtained ' + self.marks + ' marks'
    
    @property
    def gotmarks(self):
        return self.name + ' obtained ' + self.marks + ' marks'

 

 

decorator를 이해하기 위한 개념들

: first-class objects / inner function / decorator

 

First-class objects

  • 일급 함수 or 일급 객체
  • 변수나 데이터 구조에 할당이 가능한 객체
  • 파라미터로 전달 가능 + 리턴 값으로 사용
  • 파이썬의 함수는 일급 함수

 

  • 예시
def square(x):
	return x*x

def formula(method, argument_list):    # 함수(method)를 파라미터로 사용
	return [method(value) for value in argument_list]

f = formula    # 함수(formula)를 변수로 사용
f(square, [1,2,3])    # [1, 4, 9]

 

 

Inner function

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

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

print_msg('Hello, Python')

 

Closure

inner function 을 return값으로 반환

def print_msg(msg):
    def printer():
        print(msg)
    return printer

another = print_msg('Hello, Python')
another()

Decorator function

복잡한 클로저 함수를 간단하게 구현

 

  • 예시 1
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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
'''

 

  • 예시 2
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
  • 위 코드가 돌아가는 순서 해석 코드
def generate_power(exponent):
    print('1 :', exponent)
    def wrapper(f):
        print('2 :', f)
        def inner(*args):
            print('3 :', *args)
            result = f(*args)
            print('4 :', result)
            print('5 :', exponent**result)
            return exponent**result
        print('6 :', inner)
        return inner
    print('7 :', wrapper)
    return wrapper

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

print(raise_two(7))

'''
실행결과:

1 : 2
7 : <function generate_power.<locals>.wrapper at 0x7fa5331d8320>
2 : <function raise_two at 0x7fa5331e3f80>
6 : <function generate_power.<locals>.wrapper.<locals>.inner at 0x7fa5331e3440>
3 : 7
4 : 49
5 : 562949953421312
562949953421312
'''

 

 

 

참고

*args, **kwargs에 대한 설명 참고 링크

 

[나름 중급 파이썬1] *args와 **kwargs

항상 헷갈리는 두 가지 다시 한번 살펴보자 | 이 글은 파이썬의 문법을 모르면 이해하기 어렵습니다. python의 함수 작성 요령, 인자(argument)와 파라미터를 이해한다면 도움이 되는 내용입니다. 아

brunch.co.kr

 

 

728x90