hojeomi blog

4-1. 파이썬 OOP(객체 지향 프로그래밍) 본문

AI/Course

4-1. 파이썬 OOP(객체 지향 프로그래밍)

호저미 2021. 1. 22. 00:04
Day 4-1

1. 객체지향 프로그램

  • 예전에 만든 코드를 재사용할 수 있게 해줌
  • Object-Oriented Programming, OOP
  • 객체: 실생활에서 일종의 물건 → 속성(attribute)와 행동(action)을 가짐
  • OOP는 이러한 객체 개념을 프로그램으로 표현 → 속성은 변수(variable), 행동은 함수(method)로 표현됨
  • 예) variable: 선수, method: 공을 차다
  • OOP는 설계도에 해당하는 클래스(class)와 구현체인 인스턴스(instance)로 나뉨

1) 클래스의 매직 메소드: init, str, add, eq

In [25]:
class SoccerPlayer(object):
    
# attribute 추가는 __init__, self와 함께
# __init__은 객체 초기화 예약 함수

    def __init__(self, name:str, position:str, back_number:int):  # :<타입>을 적어주면 변수 선언 시 힌트
        self.name = name
        self.position = position
        self.back_number = back_number
        
    def __str__(self):
        return "hello!"
    
    def __add__(self, other):
        return self.name + other.name
    
    def change_back_number(self, new_number):
        print(f"선수의 등번호를 {self.back_number}에서 {new_number}로 변경합니다.")
        self.back_number = new_number
In [26]:
a = SoccerPlayer('son','FW',7)
b = SoccerPlayer('park','WF',11)
a
Out[26]:
<__main__.SoccerPlayer at 0x22ed65a8580>
In [27]:
# __str__의 값이 print를 하면 나옴

print(a)
hello!
In [28]:
# __add__ 실행

a + b
Out[28]:
'sonpark'
In [29]:
a.change_back_number(99)
선수의 등번호를 7에서 99로 변경합니다.
In [41]:
# __init__에서 속성 불러오기

a.back_number = 20
print(a.back_number)
20
In [ ]:
 
In [ ]:
 

2. 상속(Inheritance)

  • 부모클래스로부터 속성과 method를 물려받은 자식 클래스를 생성하는 것
In [60]:
class Person(object):
    
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        
    def about_me(self):
        print(f"저의 이름은 {self.name}이구요, 제 나이는 {self.age}살 입니다.")
        
    def about_parent(self):
        print("부모클래스로 연결된다.")
In [61]:
class Employee(Person):
    
    def __init__(self, name, age, gender, salary, hire_date):
        super().__init__(name, age, gender)  # super()로 부모클래스의 속성을 받아옴
        self.salary = salary
        self.hire_date = hire_date
        
    def about_me(self):
        super().about_me()
        print(f"제 급여는 {self.salary}입니다.")     
In [62]:
p1 = Person('jm','25','F')
In [63]:
p1.about_me()
저의 이름은 jm이구요, 제 나이는 25살 입니다.
In [71]:
p1_job = Employee('modi','52','F','30000','2021-01-01')
In [74]:
p1_job.about_me()  # 부모클래스와 연결됨
저의 이름은 modi이구요, 제 나이는 52살 입니다.
제 급여는 30000입니다.

* Employee 클래스에 없어도 부모클래스의 함수를 사용할 수 있음 (Empployee("Person")으로 object에 부모클래스를 넣어주기 때문?)

In [75]:
p1_job.about_parent()
부모클래스로 연결된다.
In [ ]:
 
In [ ]:
 

3. 다형성(Polymorphism)

  • 같은 이름 메소드의 내부 로직을 다르게 작성
  • Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모클래스의 상속에서 주로 발생함
  • [참고] 동적 타이핑: a = 3 이라고 선언하면 a의 타입은 int형으로 자동으로 정해짐(int a = 3은 정적 타이핑)
  • OOP의 개념 중 하나

🎈 Question. 상속과 무슨 차이? → 상속과 같은 기능들로 생긴 클래스의 특성 중 하나

In [76]:
class Animal:
    
    def __init__(self, name):
        self.name = name
    
    def talk(self):
        raise NotImplementedError("Subclass must implement abstract method.")
In [85]:
class Cat(Animal):
    def talk(self):
        return "야옹"
    
class Dog(Animal):
    def talk(self):
        return "월월"
In [86]:
duck = Cat("duck")
daebak = Dog("daebak")
In [87]:
duck.talk()
Out[87]:
'야옹'
In [88]:
daebak.talk()
Out[88]:
'월월'
In [ ]:
 
In [ ]:
 

4. 다형성(Visibility)

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

[알아두면 상식] Encapsulation

  • 캡슐화 또는 정보 은닉(Information Hiding)
  • 클래스를 설계할 때, 클래스 간 간섭/정보공유의 최소화
  • 심판 클래스가 축구선수 클래스 가족 정보를 알아야 하나?
  • 캡슐을 던지듯, 인터페이스만 알아서 써야함

1) 예제

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

#1. 언더바 두 개(__) 없을 경우(private 변수가 아닐 경우)

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

class Inventory(object):
    
    def __init__(self):
        self.items = []
        self.test = "abc"
        
    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 [108]:
my_inventory = Inventory()
my_inventory.add_new_item(Product())
new item added
In [109]:
my_inventory.items  # items에는 Product(내가 선언해준 클래스) 타입인 것만 들어갈 수 있음
Out[109]:
[<__main__.Product at 0x22ed65bdc10>]
In [110]:
my_inventory.items.append("def")  # 하지만, items에 외부로부터의 접근도 허용되기에 Product 타입이 아닌 것도 들어가게 됨
In [111]:
my_inventory.items  # 이런 경우를 방지하기 위해 private 변수 생성 필요
Out[111]:
[<__main__.Product at 0x22ed65bdc10>, 'def']

#2. 언더바 두 개(__) 있을 경우(private 변수일 경우)

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

class Inventory(object):
    
    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)
In [116]:
my_inventory = Inventory()
my_inventory.add_new_item(Product())
new item added
In [119]:
my_inventory.__items  # private로 숨겨서 접근이 안됨
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-119-5dd8f41ca11f> in <module>
----> 1 my_inventory.__items  # private로 숨겨서 접근이 안됨

AttributeError: 'Inventory' object has no attribute '__items'
In [121]:
my_inventory.__items.append("def")  # 다른 객체 추가도 안됨
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-121-29cd4f8025e9> in <module>
----> 1 my_inventory.__items.append("def")  # 다른 객체 추가도 안됨

AttributeError: 'Inventory' object has no attribute '__items'

2) 예제

  • 조건
    1) Product 객체를 Inventory 객체에 추가
    2) Inventory에는 오직 Product 객체만 들어감
    3) Inventory에 Product가 몇 개인지 확인이 필요
    4) Inventory에 Product items는 접근은 가능 (위 예제와 차이점)

→ property decorator 이용: 숨겨진 변수를 반환하게 해줌

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

class Inventory(object):
    
    def __init__(self):
        self.__items = []  ## 언더바 두 개(__) -> private 변수 생성, 타 객체가 접근 못함
        
    @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)
In [123]:
my_inventory = Inventory()
my_inventory.add_new_item(Product())
new item added
In [126]:
my_inventory.items  # 언더바 두 개 빼고 실행하면 property decorator를 통해 접근 가능
Out[126]:
[<__main__.Product at 0x22ed65bdee0>]
In [ ]:
 
In [ ]:
 

5. decorate

1) first-class object

  • 일등함수 또는 일급 객체
  • 변수나 데이터 구조에 할당이 가능한 객체
  • 파라미터로 전달이 가능 + 리턴 값으로 이용
    파이썬의 함수는 일급함수
    예) map(f, ex)에서 f는 함수이지만 파라미터로 쓰임
In [3]:
def square(x):
    return x*x

f = square  # 함수를 변수로 사용
print(f(5))
25
In [4]:
def cube(x):
    return x*x*x
In [5]:
def formula(method, argument_list):
    return [method(value) for value in argument_list]
In [9]:
a = formula(square,[1,2,3])  # 함수를 파라미터로 사용
print(a)
[1, 4, 9]

2) 내재함수(inner function)

In [12]:
def print_msg(msg):  # 1
    def printer():  #3
        print(msg)
    printer()  #2
In [13]:
print_msg("hello")
hello
  • closures: inner function을 return값으로 반환
In [14]:
def print_msg(msg):  # 1
    def printer():  #3
        print(msg)
    return printer()  #2
In [25]:
print_msg("hello")
hello

2-a) 예제 - closures

🎈 Qustion. # ()를 붙이는 것과 안 붙이는 것의 차이?

#1. () 붙인 것

In [37]:
def tag_func(tag, text):
    text = text
    tag = tag
    
    def inner_func():
        print('<{0}>{1}<{0}>'.format(tag, text))
        return ('<{0}>{1}<{0}>'.format(tag, text))
    
    return inner_func()  # ()를 붙이는 것과 안 붙이는 것의 차이

h1_func = tag_func('title',"This is class")
p_func = tag_func('p',"Data academy")
<title>This is class<title>
<p>Data academy<p>
In [38]:
h1_func
Out[38]:
'<title>This is class<title>'

#2. () 안 붙인 것

In [39]:
def tag_func(tag, text):
    text = text
    tag = tag
    
    def inner_func():
        print('<{0}>{1}<{0}>'.format(tag, text))
        return ('<{0}>{1}<{0}>'.format(tag, text))
    
    return inner_func  # ()를 붙이는 것과 안 붙이는 것의 차이

h1_func = tag_func('title',"This is class")
p_func = tag_func('p',"Data academy")
In [40]:
h1_func
Out[40]:
<function __main__.tag_func.<locals>.inner_func()>

2-b) 예제

#1.

In [60]:
def star(func):
    def inner(*args, **kwargs):
        print("*"*30)
        func(*args, **kwargs)  # func -> msg -> "hello"
        print("*"*30)
    return inner

@star
def printer(msg):
    print(msg)
In [61]:
printer("hello")
******************************
hello
******************************
In [62]:
def percent(func):
    def inner(*args, **kwargs):
        print("%"*30)
        func(*args, **kwargs)
        print("%"*30)
    return inner

@star
@percent  # percent 먼저, 그 다음 star
def printer(msg):
    print(msg)
In [63]:
printer("hello")
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

#2.

In [64]:
def star(func):
    def inner(*args, **kwargs):
        print(args[1]*30)  # args[1] -> mark
        func(*args, **kwargs)
        print(args[1]*30)
    return inner

@star
def printer(msg, mark):
    print(msg)
In [65]:
printer("hello","T")
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
hello
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

#3.

In [73]:
def generate_power(exponent):  
    
    def wrapper(f):  # f -> raise_two
        def inner(*args):  
            result = f(*args)  # f() -> n^2 예) 7^2
            print(result)
            return exponent**result  # exponent -> 2 => 2^49가 리턴됨 
        return inner
    
    return wrapper
In [74]:
@generate_power(2)
def raise_two(n):
    return n**2
In [75]:
print(raise_two(7))
49
562949953421312
In [ ]:
 

'AI > Course' 카테고리의 다른 글

Day 2-2. 파이썬 함수에서 call by object reference  (0) 2021.01.25
Day 5-1. File / Exception / Log Handling  (0) 2021.01.22
3-2. Pythonic Code  (0) 2021.01.21
Day 3-1. Python Data Structure  (0) 2021.01.20
Day 2. 파이썬 기초 문법  (2) 2021.01.19
Comments