* Python Object Oriented Programming
💡 아래 내용은 부스트코스(boostcourse) 인공지능(AI) 기초 다지기 강의를 듣고 공부하며 내용을 정리한 글입니다. 더 자세한 내용은 실제 강의를 들어보길 추천합니다 😃
📝 Contents
- 객체 지향 프로그래밍(Object Oriented Programming_OOP)
- Class 구현 방법
- OOP 구현 방법
- 객체 지향 언어의 특징: 상속(Inheritance), 다형성(Polymorphism), 가시성(Visibility)
- decorate 이해하기 위한 개념들(일급 객체(first-class object), Inner Function, Closer)
🐍 객체 지향 프로그래밍(Object Oriented Programming_OOP)
- 객체: 실생활에서 일종의 물건, 속성(Attribute)과 행동(Action)을 가짐
- OOP는 이러한 객체 개념을 프로그램으로 표현
=> 속성은 변수(variable), 행동은 함수(method)로 표현됨
- 파이썬 역시 객체 지향 프로그래밍 언어
자주 나오는 예시의 {클래스와 인스턴스 : 붕어빵틀과 붕어빵}
처음 코딩 책을 볼 때 자꾸 '인스턴스(Instance)'라는 용어가 나오는데 어렵게 느껴져 그 개념을 잘 이해 못 했었다. 하지만 위 붕어빵 예시처럼 나는 Instance를 원래 영어 자체가 '사례, 예시'라는 의미로 정해진 (Class) 코드 안에서 나오는 다양한 예시들, 경우라고 나는 내 식대로 받아들였다.
다양한 붕어빵(사례들)이 나올 수 있다 🤣
실제 구현해보기 - 축구 선수 정보를 Class로 구현하기
# Class 함수명은 CamelCase 사용
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 to %d" % \
(self.back_number, new_number))
self.back_number = new_number
def __str__(self):
return "Hello, My name is %s, My back number is %d" %\
(self.name, self.back_number)
def __add__(self, other):
return self.name + other.name
son = SoccerPlayer("son","FW", 7)
park = SoccerPlayer("park","WF", 13)
son is park # false (서로 다른 객체)
son + park # 'sonpark'
print(son) # Hello, My name is son, My back number is 7
choi = SoccerPlayer("Jinhyun", "mf", 10)
print(choi) # Hello, My name is Jinhyun, My back number is 10
choi.change_back_number(7)
print(choi)
# 선수의 등번호를 변경합니다 : From 10 to 7
# Hello, My name is Jinhyun, My back number is 7
# 이렇게 객체의 속성을 직접 바꿔줄 수도 있지만 위 방식을 권장함
choi.back_number = 13
print(choi) # Hello, My name is Jinhyun, My back number is 13
class 구현
class 이름(상속받는 객체명):
Attribute 추가는 __init__, self와 함께!
'__' 는 특수한 예약 함수나 변수, 함수명 변경(맨글링)으로 사용
- method(Action) 추가는 기존 함수와 같으나 반드시 self를 추가해야만 class 함수로 인정된다.
OOP 구현
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
class NoteBook(object):
def __init__(self, title):
self.title = title
self.page_number = 1
self.total_page = 1
self.notes = {}
def add_note(self, note, page = 0):
if self.page_number <= 300 and page <= 300:
if page == 0:
self.notes[self.page_number] = note
self.page_number += 1
self.total_page += 1
else:
self.notes[page] = note
#self.notes = {page : note}
self.total_page += 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())
🌟 객체 지향 언어의 특징 - 상속(Inheritance), 다형성(Polymorphism), 가시성(Visibility)
: 실제 세상을 모델링
상속(Inheritance)
- 부모 클래스로부터 속성과 Method를 물려받은 자식 클래스를 생성하는 것
class Person(object):
def __init__(self):
self.name = "SJ"
self.age = 20
class Player(Person):
pass
the_player = Player()
print(the_player.name) # SJ
클래스 Player는 Person 부모 클래스로부터 속성, Method를 물려받는다.
class Person(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def about_me(self):
print("저의 이름은", self.name, "이구요, 제 나이는", str(self.age), "살 입니다.")
class Korean(Person):
pass
first_korean = Korean("sungchul", 35, "man")
print(first_korean.about_me()) # 저의 이름은 sungchul 이구요, 제 나이는 35 살 입니다.
(함수의 리턴값이 없기 때문에 None도 뒤에 같이 출력되지만 이는 생략)
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, " 입니다.")
manager = Employee("choi", 23, "women", 450, "22/12/20")
print(manager.do_work())
# 열심히 일을 합니다.
print(manager.about_me())
# 저의 이름은 choi 이구요, 제 나이는 23 살 입니다.
# 제 급여는 450 만원이고, 제 입사일은 22/12/20 입니다.
다형성(Polymorphism)
- 같은 이름 메소드의 내부 로직을 다르게 작성
- Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모 클래스의 상속에서 주로 발생
class Animal:
def __init__(self, name):
self.name = name
def talk(self):
raise NotImplementedError("Subclass must implement abstract method")
class Cat(Animal):
def talk(self):
return "Meow!"
class Dog(Animal):
def talk(self):
return "mung! mung!"
같은 이름 메서드(talk)의 내부 로직(return 값)을 다르게 작성!
animals = [Cat('Cheese'),
Cat("Samsag"),
Dog("Kimza")]
for animal in animals:
print(animal.name + ": " + animal.talk())
"""
Cheese: Meow!
Samsag: Meow!
Kimza: mung! mung!
"""
가시성(Visibility)
- 객체의 정보를 볼 수 있는 레벨을 조절하는 것
- 🌟 누구나 객체 안에 모든 변수를 볼 필요가 없음 => 캡슐화(Encapsulation) 또는 정보 은닉(Information Hiding)
1) 객체를 사용하는 사용자가 임의로 정보 수정하면 x
2) 필요 없는 정보에는 접근할 필요가 없음
3) 만약 제품으로 판매한다면? 소스의 보호
- 클래스를 설계할 떼, 클래스 간 간섭, 정보 공유의 최소화
- __ mangling 접근 못함
- @property 함수명을 변수명처럼 사용하게 해 줌 => 접근 가능
(가시성 예시)
class Product(object):
def __init__(self):
self.__items = []
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)
self.__items => Private 변수로 선언, 타 객체가 접근 못함
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.append(Product())
print(my_inventory.get_number_of_items())
"""
new item added
new item added
2
AttributeError: 'Inventory' object has no attribute 'items'
"""
조건 수정 : Inventory에 Product items 접근 허용
class Product(object):
def __init__(self):
self.__items = []
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
@property
property decorator 숨겨진 변수를 반환하게 해 준다.
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 # Property decorator로 함수를 변수처럼 호출
items.append(Product())
print(my_inventory.get_number_of_items())
"""
new item added
new item added
2
3
"""
※ decorate 이해하기 위한 개념들
1. 일등 함수 또는 일급 객체 (first-class object)
- 변수나 데이터 구조에 할당이 가능한 객체
- 파라미터로 전달이 가능, 리턴 값으로 사용
- 파이썬의 함수는 일급 함수
# 함수를 변수로 사용
def square(x):
return x * x
f = square
f(5) # 25
# 함수를 파라미터로 사용
def cube(x):
return x*x*x
def formula(method, argument_list):
return [method(value) for value in argument_list]
formula(cube, [1, 2, 3]) # [1, 8, 27]
2. Inner Function
# 함수 내에 또 다른 함수가 존재
def print_msg(msg):
def printer():
print(msg)
printer()
print_msg("Hello, Python") # Hello, Python
- Closer: inner function을 return값으로 반환
def print_msg(msg):
def printer():
print(msg)
return printer
another = print_msg("Hello, Python")
another() # Hello, Python
클로저란?
자신을 둘러싼 scope(name space)의 상태값을 기억하는 함수
어떤 함수가 클로저이기 위해서는 다음의 세 가지 조건을 만족해야 한다.
1. 해당 함수는 어떤 함수 내의 중첩된 함수여야 한다.
2. 해당 함수는 자신을 둘러싼(enclose) 함수 내의 상태값을 반드시 참조해야 한다.
3. 해당 함수를 둘러싼 함수는 이 함수를 반환해야 한다.
[출처 : https://shoark7.github.io/programming/python/closure-in-python#3a]
클로저 예시)
def tag_func(tag, text):
text = text
tag = tag
def inner_func():
return '<{0}>{1}<{0}>'.format(tag, text)
return inner_func
hi_func = tag_func('title', 'This is Python Class')
p_func = tag_func('p', "Data Academy")
print(hi_func()) # <title>This is Python Class<title>
p_func() # '<p>Data Academy<p>'
3. decorator
# 복잡한 클로져 함수를 간단하게!
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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
"""
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(1))
print(raise_two(2)) # 2 ** 2 = 4 => 2 ** 4승 = 16
print(raise_two(7)) # 7 ** 2 = 49 => 2 ** 49승 = 562949953421312
[키워드 가변인자(*args) 참고: 2023.01.23 - [Python] - [인공지능(AI) 기초 다지기] Pythonic Code 파이썬다운 코드를 작성해보자! - list comprehension, enumerate, zip, lambda, generator, asterisk(*) ..]
+ 그동안 강의 중 꽤 어려운 내용이 나왔다. 내용이 꽤 많아 정리할까 고민했지만, 역시 정리하면서 다시 보면 스스로 공부가 제일 많이 된다(내 블로그를 제일 자주 찾는 건 나다). 같은 강의도 세부적으로 보면 공부해야 할 내용이 정말 많다.
이 강의는 원래 이쪽 분야 지식이 없는 사람들도 들을 수 있도록 전반적으로 설명해 주어 여러 가지 면에서 복습도 되고 개념을 명확히 해줘 너무 좋다 :)
🙂 공감과 피드백 환영합니다.