Python OOP Tutorial 강의 요약 : Class 제대로 활용하기

Joey Lee·2020년 12월 5일
0

Python

목록 보기
10/12

Python OOP Tutorial 유투브 강의 링크

Jupyter Notebook에 연습 코드 작성했으니 참고할 것

1. Classes and instances

  • 클래스는 왜 필요한가?
  • 클래스 attribute는 무엇인가?
    => 클래스에 소속된 변수임
    => 클래스 자체에 속하는 변수가 있을 수 있고, 인스턴스에 속하는 변수가 있을 수 있음
    => 인스턴스 생성 시 바로 포함되어야 할 데이터는 생성자(init)로 정의해서 사용함
  • 클래스 method는 무엇인가?
    => 클래스에 소속된 함수임
    => 첫 번째 인자로 인스턴스가 들어감
    => 클래스 선언 시에 둘 다 self가 첫 번째 인자여야 함
  • 클래스와 인스턴스는 어떤 관계인가? 생성하고, 호출해서 사용할 때 어떤 차이점이 있는가?
class Employee:

  def __init__(self, first, last, pay):
    self.first = first  
    self.last = last
    self.email = first + '.' + last + '@email.com'
    self.pay = pay

    # 인자로 self가 있어야 함. 없다면, instance 호출 시 에러 발생 (왜냐하면 instance가 자동으로 인자로 전달되기 때문임)
  def fullname(self):    
    return '{} {}'.format(self.first, self.last)

emp_1 = Employee('Corey', 'Schafer', 50000)
emp_2 = Employee('Test', 'Employee', 60000)

emp_1.fullname()   # 메소드 호출 시 ()를 잊으면 안 됨
Employee.fullname(emp_1)

2. Class variables

  • 클래스 변수와 인스턴스 변수의 차이에 대해서 이해하기
  • 클래스 메소드에서 클래스 변수(Employee.raise_amount)와 인스턴스 변수(self.raise_amount)를 이용하는 것의 차이에 대해서 이해하기
class Employee:
  
  num_of_emps = 0
  raise_amount = 1.04   # 이게 바로 클래스 변수
  
  def __init__(self, first, last, pay):
    self.first = first
    self.last = last
    self.email = first + '.' + last + '@email.com'
    self.pay = pay
    
    Employee.num_of_emps += 1
    
  def fullname(self):    
      return '{} {}'.format(self.first, self.last)
  
  def apply_raise(self):
    self.pay = int(self.pay * self.raise_amount)   
    # raise_amount는 클래스/인스턴스로 호출 가능 (클래스 => 클래스의 변수를 가져오고, 인스턴스는 인스턴스 변수값을 가져옴)

# 인스턴스 2개 생성 후 Employee 수 확인 (클래스의 변수임)
emp_1 = Employee('Corey', 'Schafer', 50000)
emp_2 = Employee('Test', 'Employee', 60000)
print(Employee.num_of_emps)

#. 인스턴스의 변수를 변경하면 해당 인스턴스에만 영향. 클래스는 변화 없음.
emp_1.apply_raise()
print(emp_1.pay)

# 클래스 변수를 바꾸면 인스턴스에는 다 적용됨
Employee.raise_amount = 1.05
print(Employee.raise_amount)
print(emp_1.raise_amount)
print(emp_2.raise_amount)

# 위의 내용을 확인해 볼 수 있는 방법(해당 인스턴스/클래스의 키/밸류 알 수 있음)
print(emp_1.__dict__)     # 클래스에서 정의해 준 것을 그대로 가져오기에 인스턴스에는 raise_amount 속성이 없음
print(Employee.__dict__)  # 클래스에서는 raise_amount 속성을 확인할 수 있음

3. Class methods and static methods

1) Class methods

  • 클래스 메소드의 첫 번째 인자는 self, 즉 인스턴스라고 했음
  • 인스턴스가 아니라 클래스가 인자가 되는 것이 클래스 메소드임.
    1) 클래스 메소드는 특정 인스턴스에 귀속되는 것이 아니기에 클래스 변수를 조정할 때 사용할 수 있음
    2) 클래스 메소드는 alternative constructor로 주로 사용됨. 가령, 특정한 데이터를 파싱해서 인스턴스를 바로 만들고자 할 때 사용할 수 있음.

2) Static methods

  • static method는 일반 함수와 같음. 인스턴스나 클래스가 인자로 들어가지 않음
  • 해당 클래스에 연결해서 유용하게 사용될 함수를 추가할 때 사용하면 됨
class Employee:

    num_of_emps = 0
    raise_amt = 1.04

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)

    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amt)

    @classmethod
    def set_raise_amt(cls, amount):
        cls.raise_amt = amount

    @classmethod
    def from_string(cls, emp_str):
        first, last, pay = emp_str.split('-')
        return cls(first, last, pay)

    @staticmethod
    def is_workday(day):
        if day.weekday() == 5 or day.weekday() == 6:
            return False
        return True


emp_1 = Employee('Corey', 'Schafer', 50000)
emp_2 = Employee('Test', 'Employee', 60000)

Employee.set_raise_amt(1.05)

print(Employee.raise_amt)
print(emp_1.raise_amt)
print(emp_2.raise_amt)

emp_str_1 = 'John-Doe-70000'
emp_str_2 = 'Steve-Smith-30000'
emp_str_3 = 'Jane-Doe-90000'

first, last, pay = emp_str_1.split('-')

#new_emp_1 = Employee(first, last, pay)
new_emp_1 = Employee.from_string(emp_str_1)

print(new_emp_1.email)
print(new_emp_1.pay)

import datetime
my_date = datetime.date(2016, 7, 11)

print(Employee.is_workday(my_date))

4. Subclass and inheritance

  • Employee를 가지고 Developer와 Manager라는 서브클래스를 생성
  • 특정 클래스가 여러 개의 클래스를 상송했을 때 어떤 것을 더 우선순위를 부여할지에 대해 Method Resolution Order로 규정함
  • super()를 통해 부모 클래스의 요소를 그대로 가져와서 사용할 수 있음.
    -> super() 대신 부모클래스명으로 바꿔도 됨
  • isinstance(intance, class) : instance는 class의 인스턴스인지를 True/False로 리턴
  • issubclass(subclasse, class) : subclass는 class의 서브클래스인지를 True/False로 리턴
  • 예) BadRequest는 HttpException을 상속받음. HttpException은 Exception을 상속받은 것임
class Employee:

    raise_amt = 1.04

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

    def fullname(self):
        return '{} {}'.format(self.first, self.last)

    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amt)


class Developer(Employee):
    raise_amt = 1.10

    def __init__(self, first, last, pay, prog_lang):
        super().__init__(first, last, pay)
        self.prog_lang = prog_lang


class Manager(Employee):

    def __init__(self, first, last, pay, employees=None):
        super().__init__(first, last, pay)
        if employees is None:
            self.employees = []
        else:
            self.employees = employees

    def add_emp(self, emp):
        if emp not in self.employees:
            self.employees.append(emp)

    def remove_emp(self, emp):
        if emp in self.employees:
            self.employees.remove(emp)

    def print_emps(self):
        for emp in self.employees:
            print('-->', emp.fullname())


dev_1 = Developer('Corey', 'Schafer', 50000, 'Python')
dev_2 = Developer('Test', 'Employee', 60000, 'Java')

mgr_1 = Manager('Sue', 'Smith', 90000, [dev_1])

print(mgr_1.email)

mgr_1.add_emp(dev_2)
mgr_1.remove_emp(dev_2)

mgr_1.print_emps()

5. Special(Magic/Dunder) method

  • __init__, __repr__, __str__, __add__, __len__ 등 파이썬에서 이미 제공하고 있는 메소들이 스페셜 메소드임
  • 이들 메소드는 클래스에서 본인이 원하는 방식으로 재정의해서 사용
  • 호출하는 방식은 크게 2가지 방식이 있음
print(len('test')
print('test'.__len())

print(1+2)
print(int.__add__(1,2))       # 3을 출력
print(str.__add__('a', 'b')   # ab를 출력
class Employee:

    raise_amt = 1.04

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

    def fullname(self):
        return '{} {}'.format(self.first, self.last)

    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amt)

    def __repr__(self):
        return "Employee('{}', '{}', {})".format(self.first, self.last, self.pay)

    def __str__(self):
        return '{} - {}'.format(self.fullname(), self.email)

    def __add__(self, other):
        return self.pay + other.pay

    def __len__(self):
        return len(self.fullname())


emp_1 = Employee('Corey', 'Schafer', 50000)
emp_2 = Employee('Test', 'Employee', 60000)

# print(emp_1 + emp_2)

print(len(emp_1))

6. Property Decorators - Getters, Setters and Deleters

  • 위에서 이메일은 first, last의 조합으로 구성이 됨. 만약 first를 바꿔주면 이메일은 생성자로 생성되는 것이기 때문에 업데이트가 되지 않음
  • 이걸 first가 변하면 업데이트 시켜줄려면 email() 메소드로 구현을 하면 됨
  • 이 때 이 메소드를 attribute로 사용할 수 있게 해 주는 것이 Property decorator임
  • Fullname 변경 시, 이게 쪼개져서 first, last를 수정하도록 해 줄 수 있음. 이게 바로 세터이고, @fullname.setter decorator로 해서 사용할 수 있음. (fullname이란 메소드가 있는 상태에서 동일한 이름으로 setter 메소드를 하나 더 만들어 주는 것임)
class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last

    @property
    def email(self):
        return '{}.{}@email.com'.format(self.first, self.last)

    @property
    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    @fullname.setter
    def fullname(self, name):
        first, last = name.split(' ')
        self.first = first
        self.last = last
    
    @fullname.deleter
    def fullname(self):
        print('Delete Name!')
        self.first = None
        self.last = None


emp_1 = Employee('John', 'Smith')
emp_1.fullname = "Corey Schafer"

print(emp_1.first)
print(emp_1.email)
print(emp_1.fullname)

del emp_1.fullname
profile
안녕하세요!

0개의 댓글