[Python] Python의 Class
객체지향#
class
- 객체 모델링의 수단
- 객체(instance)를 생성하기 위한 단위
- ADT(Abstract Data Type)
class Car(object):
# class variable
example_var = 100
# initializer
def __init__(self, maker, cc, price):
self.maker = maker # property(속성) - instance variable
self.cc = cc
self.price = price
# instance method
def get_car_info(self):
print(Car.example_var)
return self.maker + str(self.cc) + str(self.price)
car_1 = Car('kia', 1998, 10000)
car_2 = Car('Hyundai', 2399, 20000)
class variable#
class가 저장되는 공간에 저장되었다가 이 class를 기반으로 instance가 생성되고 이 variable을 호출할 때 class 저장 공간에 접근해서 변수를 가져온다
initializer#
instance가 생성될 때 초기화를 담당함
class를 기반으로 heap 영역에 메모리 공간을 할당할 수 있다.
이 확보된 메모리 공간 자체를 instance
라고 한다
위의 예제에서 car_1과 car_2안에는 instance의 메모리 주소값이 들어있다. 이를 reference variable
이라 한다.
. (dot operator)#
객체가 있다는 것 == class가 있다는 것 모든 객체는 기본적으로 변수(property)를 가지고 있고, 함수(method)를 가지고 있다. 이런 객체가 가지는 property와 method를 사용하기 위해서 이용하는 연산자 이다.
Python에서 Class의 특징#
python의 객체지향 특징 중 하나는 instance에 새로운 property나 method를 동적으로 추가하는게 가능하다.
car_1.color = 'Red'
아이러니하게 객체지향과는 거리가 있는 특징이다. 이런 특징은 프로그램을 상당히 유연하게 작성할 수 있게 만들어준다.
하지만 여기서 발생할 수 있는 문제가 있는데 바로 scope
문제 이다.
변수를 찾는 순서는 instance namespace
, class namespace
, superclass namespace
순서이다.
# 결과가 어떻게 나올까요?
car_1.example_var = 300
car_2.get_car_info()
car_1.example_car
가 동작하는 과정에서 instance namespace
를 check하고 같은 공간에 example_var
가 없다는 것을 확인 후 python의 특징에 따라 동적으로 새로운 instance variable
을 생성해버려서 차이점이 생겨버린다.
직접 class variable을 변경하기 위해서는 class내의 제어하는 method를 통해 직접 접근하여 제어를 하면 된다.
예시#
class Employee(object):
raise_rate = 1.1 # class variable(기본 연봉 인상률)
def __init__(self, u_name, u_pay):
# instance variable
self.u_name = u_name
self.u_pay = u_pay
# business method(여기서는 사람마다 다르기 때문에 instance method로 만들 것임)
def apply_raise(self):
self.u_pay = self.u_pay * self.raise_rate
def get_user_info(self):
return "현재 {}의 연봉은 {}입니다.".format(self.u_name, self.u_pay)
# decorator를 이용해서 class method를 정의해야 해요!
@classmethod
def change_raise_rate(cls, rate): # class method이기 때문에 instance를 지칭하는 self가 아닌 class를 지칭하는 cls가 나와야함
# 값이 들어올 때 class referance가 들어오기 때문에 바로 class variable로 지칭이 된다.
cls.raise_rate = rate
print('기본 연봉 인상률이 {}으로 조정되었습니다.'.format(cls.raise_rate))
# 보통 class안에서 instance method와 class method는 각각 역할이 있는데.. property를 제어하지 않고 단일 동작하는 method를 작성할때는 어떻게 할까??
# decorator를 통해 static method로 작성하면 된다.
# @staticmethod
# def my_func():
emp_1 = Employee('Kim', 30000000)
emp_2 = Employee('Kang', 20000000)
# 연봉인상 전 두 사람의 정보를 출력
print(emp_1.get_user_info())
print(emp_2.get_user_info())
# 연봉인상률을 변경해요
Employee.change_raise_rate(1.5)
# emp_1.apply_raise()
# print(emp_1.get_user_info())
# emp_1.change_raise_rate(1.7)
# 연봉인상률을 적용해요
emp_1.apply_raise()
emp_2.apply_raise()
# 변경된 인상률을 출력해요
print(emp_1.get_user_info())
print(emp_2.get_user_info())
# emp_1.__u_mobile = '1234-5965'
# 이런식으로 direct access를 할 수 없도록 제한할 수 없나요?
# 이럴 때 property 앞에 __를 붙이면 java에서 사용되는 private 역할을 수행할 수 있습니다. 혹은 _를 붙이면 protected로 수행합니다. 이는 python 표준이지만 jupyter는 그렇지 않나봅니다..
객체지향의 꽃 상속(Inheritance)#
class를 상속해서 다른 class를 확장시키는 기법이다.
부모 class => parent class, super class, upper class 자식 class => child class, sub class
# 부모 class
class Unit(object):
def __init__(self, damage, life):
self.damage = damage # 공격력
self.life = life # 생명력
# 상속이 없다면??
# class Tank(object):
# def __init__(self, damage, life, missile_ammo):
# self.damage = damage # 공격력
# self.life = life # 생명력
# self.missile_ammo = missile_ammo # 미사일 탄창수?
# 상속을 사용하면?
class Tank(Unit): # Inheritance
# pass
def __init__(self, damage, life, missile_ammo):
super(Tank, self).__init__(damage, life)
self.missile_ammo = missile_ammo
tank_1 = Tank(100, 50, 300)
tank_1.damage = 1000
magic method#
보통 method 이름 앞에 __가 붙은 method를 지칭하며 class 안에 미리 정의된 특수한 기능을 하고 있는 method이다 (ex. init())
class Part_time(Employee):
# initializer역할(초기화 역할)
def __init__(self, name, pay, part):
super(Part_time, self).__init__(name, pay)
self.u_part = part
print('생성자가 호출되었어요!')
# 소멸자
def __del__(self):
print('소멸자가 호출되었어요!')
# instance를 문자열화 시킬때 호출되는 method
def __str__(self):
return '뿡뿡이'
def get_part_time(self):
return '파트는 {} 입니다.'.format(self.part)
emp_3 = Part_time('Kong', 10000000, '편의점')
print(emp_3)
first class(일급 클래스)#
first-class citizen
: 프로그래밍 개체(함수, 객체, 변수, 등등)들이 다음의 조건을 충족하면 기본적으로 first-class citizen이라고 한다.
- 변수에 저장될 수 있다
- 함수의 인자로 전달될 수 있어야 한다
- 함수의 결과로 리턴될 수 있어야 한다.
앞에서 지금까지 설명한 class로 부터 파생된 instance는 1급 객체라고 불린다. functions도 first class 조건을 만족 == python의 함수는 1급 함수 단, 다른 언어에서는 아닐 수도 있다. (ex. C에서는 아님)
def my_add(x, y):
return x + y
print(my_add(10, 20)) # result : 30
# 1. 변수에 저장될 수 있어요!
f = my_add
print(f(100, 200)) # result : 300
# 2. 함수의 인자로 전달될 수 있어야 해요!
def my_mul(x, y):
return x * y
def my_operation(func, x, y):
result = func(x, y)
return result
print(my_operation(my_mul, 10, 20)) # result : 200
# 3. 함수의 결과로 리턴될 수 있어야 해요!
def addMaker(x): # x는 지역변수
def my_add_maker(y):
return x + y
return my_add_maker
add_5 = addMaker(5)
add_10 = addMaker(10)
print(add_5(1)) # result : 6 # 이상한데?
print(add_10(1)) # result : 11 # 머선129?
# => Closure 현상 = 1급 함수에서 나타나는 현상