[DAY 4] Object-Oriented Programming (OOP)
객체지향언어의 특징
- Inheritance(상속)
- Polymorphism(다형성)
- Visibility(Hidden class, 가시성)
Inheritance (상속)
- 부모클래스로 부터 속성과 Method를 물려받은 자식 클래스를 생성 하는 것
super
: 자신의 부모 클래스self
: 자기자신(클래스,객체)1 2 3 4 5 6 7 8 9 10
class Person(object): def __init__(self, name, age): self.name = name self.age = age class Korean(Person): # 상속을 받았기 때문에, Person 내부의 속성을 사용가능 pass first_korean = Korean("Sungchul", 35) print(first_korean.name)
inheritance example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# class Person: # 이렇게 써도 됨 # 따로 상속받지 않은 클래스는 자동으로 Object class를 상속받음 class Person(object): # 부모 클래스 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 특성으로 인해 파이썬에서는 같은 부모클래스의 상속에서 주로 발생함
- 같은 일을 하지만, 세부 구현이 다른 경우
- 같은 이름, 다른 역할
같은 이름을 쓰되, 각각의 목적에 따라 다르게 구현하는 것
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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())
Visibility(가시성)
- 객체의 정보를 볼 수 있는 레벨을 조절하는 것
- 누구나 객체 안에 모든 변수를 볼 필요가 없음
- 1) 객체를 사용하는 사용자가 임의로 정보 수정
- 2) 필요 없는 정보에는 접근 할 필요가 없음
- 3) 만약 제품으로 판매한다면? 소스의 보호
- Encapsulation (비슷한 용어)
- 캡슐화 또는 정보 은닉(Information Hiding)
- Class를 설계할 때, 클래스 간 간섭/정보공유의 최소화
- 심판 클래스가 축구선수 클래스 가족 정보를 알아야 하나?
- 캡슐을 던지듯, 인터페이스만 알아서 써야함
- 사용법만 알고있다면 내 코드를 누구나 마음대로 쓸 수 있음
- Visibility Example 1
- Product 객체를 Inventory 객체에 추가
- Inventory에는 오직 Product 객체만 들어감
- Inventory에 Product가 몇 개인지 확인이 필요
- Inventory에 Product items는 직접 접근이 불가
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
class Product(object): pass class Inventory(object): def __init__(self): self.__items = [] # private 변수로 선언, 타 객체가 변수에 접근하지 못함 def add_new_item(self, product): if type(product) == 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()) # 가능 # self.items = [] my_inventory.items.append("abc") # 가능 data = my_inventory.items # 가능 # self.__items = [] # 직접 접근, 수정 불가능 my_inventory.__items.append("abc") # 불가능 data = my_inventory.__items # 불가능 print(my_inventory.get_number_of_items()) print(my_inventory.__items) my_inventory.add_new_item(object)
- Visibility Example 2
- Product 객체를 Inventory 객체에 추가
- Inventory에는 오직 Product 객체만 들어감
- Inventory에 Product가 몇 개인지 확인이 필요
- Inventory에 Product items 접근 허용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
class Inventory(object): def __init__(self): self.__items = [] @property # property decorator: 숨겨진 변수를 반환하게 해줌 def items(self): return self.__items # 함수명을 변수명으로 쓸 수 있게 해줌 # 데이터 복사 없이, 객체 내부 변수를 바로 반환하면, 내부 변수까지 수정하는 경우 있을 수 있음 # 데이터를 복사하여 리턴하는 경우가 많음 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 = my_inventory.items # 가능(내부 접근 가능, 내부에서 접근하여(함수로) 반환 가능) my_inventory.items.append("abc") # 가능 # 값을 바꿔버릴 수 있기 때문에 아래와 같이 데이터를 복사한 후, 리턴하게 됨 """ @property def items(self): ret = self.__items[:] return ret """ items.append(Product()) print(my_inventory.get_number_of_items())
Decorate
- first-class objects(1급 함수)
- inner function
- decorator
First-class objects
- 일등함수 또는 일급객체
- 변수나 데이터 구조에 할당이 가능한 객체(객체를 변수에 할당)
- 파라미터로 전달이 가능 + 리턴값으로 사용 가능
- 파이썬의 함수는 일급함수
ex)
map(f, ex)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
def square(x): return x * x f = square # 함수를 변수로 사용 f(5) ################################## 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] # 두개의 formula를 만드는 것보다, 하나의 formula가 다른 메소드를 활용할 수 있게 함 # 장점: 구조화 체계를 만들어 줌 # 단점: 이해하기 어려움 # formula(square, ~) # formula(cube, ~)
Inner function
함수 내에 또 다른 함수가 존재
1 2 3 4 5 6
def print_msg(msg): def printer(): print(msg) printer() print_msg("Hello, Python")
closures : inner function을 return값으로 반환
1 2 3 4 5 6
def print_msg(msg): def printer(): print(msg) return printer # inner class return # 함수를 리턴 another = print_msg("Hello, Python") # another은 함수! another()
예시(closure example)
1 2 3 4 5 6 7 8 9 10
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") # 비슷한 목적을 가지는 다양한 함수 생성 가능
Decorator function
복잡한 클로져 함수를 간단하게
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner @star # star라는 이름의 func에 def printer(msg): # 인자로 들어감 print(msg) printer("Hello") """ ****************************** Hello ****************************** """ printer("Hello", "&") """ &&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Hello &&&&&&&&&&&&&&&&&&&&&&&&&&&&&& """
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
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 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ****************************** """
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def generate_power(exponent): # 2 def wrapper(f): # f = raise_two def inner(*args): # n result = f(*args) print(result) # 49 return exponent**result return inner return wrapper @generate_power(2) def raise_two(n): return n**2 print(raise_two(7)) # 562949953421312 (= 2**49)