python 클래스!
클래스
class Car:
brand = 'hyundai' # 클래스변수
def __init__(self, name): # 생성자
self.name = name
def print_name(self):
print(self.name)
class Truck(Car):
def __init__(self, name, size):
super().__init__(name)
self.size = size
def print_name(self): # 메서드오버라이딩
print("truck name:", self.name)
def print_size(self):
print(self.size)
if __name__ == '__main__':
c1 = Car('sonata')
print(Car.brand) # hyundai
c1.print_name() # sonata
Truck.brand = 'kia'
t1 = Truck('bongo', 10)
print(Truck.brand) # kia
t1.print_name() # truck name: bongo
t1.print_size() # 10
print(Car.brand) # hyundai
클래스변수
는 클래스 단위에서 공유되는 전역변수
Car.brand
, Truck.brand
을 출력값을 확인하면 알 수 있다, 초기값은 부모클래스에서 가져온다.
메서드
아래 2종류의 메서드에 대해 알아보자.
- 정적메서드
- 클래스메서드
클레스메서드는 좀더 기능이 추가된 정적메서드라 볼 수 있다.
class Pizza(object):
# 클래스변수
radius = 42
def __init__(self, size):
self.size = size
# 일반메서드
def get_size(self):
return self.size
# 정적메서드
@staticmethod
def mix_ingredients(x, y):
return x + y
# 클래스메서드
@classmethod
def get_radius(cls):
return cls.radius
if __name__ == '__main__':
ing = Pizza.mix_ingredients(1, 2)
rad = Pizza.get_radius()
print(ing) # 3
print(rad) # 42
클래스메서드 특징
- 객체생성 없이 바로 호출 가능
cls
클래스바인딩을 사용해 클래스 상태(변수)에 접근가능- 주로 펙토리 패턴으로 객채생성 해야할 경우 사용함
# 클레스메서드를 사용한 펙토리 패턴
@classmethod
def factory(cls, init_dict):
return cls(init_dict.name, init_dict.age)
special method
__init__, __call__
class CountCalls(object):
# 생성자
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
print("init invoked", self.num1, self.num2)
# 호출가능객체(callable object) 로 정의할 떄 사용하는 메서드
def __call__(self, keyword1, keyword2):
print("call invoked", keyword1, keyword2)
if __name__ == '__main__':
cc1 = CountCalls(1, 2) # init invoked, 1, 2
cc1("test", "print") # call invoked test print
__call__
은 인스턴스를 함수처럼 사용할 수 있게 한다.
class Adder:
def __init__(self, increment):
self.increment = increment
def __call__(self, value):
return value + self.increment
# 함수처럼 동작하는 클래스 인스턴스
add_five = Adder(5)
print(add_five(10)) # 15
# 다른 인스턴스
add_ten = Adder(10)
print(add_ten(10)) # 20
__str__, __repr__
__str__
: str은 입력 받은 객체의 문자열 버전을 반환하는 함수
__repr__
: Representation 의 약자,
추상메서드
추상메서드는 하위 클래스가 재정의하지 않으면 오류를 발생하기 위한 개념으로 자주 사용한다.
JIT
언어인 파이썬에서 아래와 같이 Pizza
객체를 생성했다고 컴파일에러를 발생시키지 않기에 쉽게 버그가 발생한다.
class Pizza(object):
pass
class CheesePizza(Pizza):
def get_name(self):
return "cheese"
if __name__ == '__main__':
p = Pizza() # 에러발생하지 않음
이때 유용하게 사용할 수 있는 패키지가 abc(abstract base class)
from abc import ABC, abstractmethod
class Pizza(ABC):
@abstractmethod
def get_name(self):
"""TODO method overriding"""
if __name__ == '__main__':
p = Pizza() # 생성자를 호출하자마자 에러가 발생
# TypeError: Can't instantiate abstract class Pizza with abstract method get_name
클래스 decorator
__call__
를 사용하면 클래스를 함수처럼 사용할 수 있기 때문에 클래스를 decorator
로 정의할 수 있다.
아래 같이 클로저를 사용하지 않아도 내부 변수를 유지하기 위해 사용할 수 있고
class CountCalls(object):
def __init__(self, f):
self.f = f
self.called = 0
def __call__(self, *args, **kwargs):
self.called += 1
return self.f(*args, **kwargs)
@CountCalls
def print_hello1():
print("hello1")
@CountCalls
def print_hello2():
print("hello2")
if __name__ == '__main__':
print_hello1()
print_hello1()
print_hello2()
print_hello2()
print_hello2()
print(print_hello1.called) # 2
print(print_hello2.called) # 3
decorator
매개변수 지정하는 것도 중첩함수 정의 없이 쉽게 정의 가능하다.
class MyDecorator:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def __call__(self, func):
def wrapped_func(*args, **kwargs):
print(f"Arguments received: {self.arg1}, {self.arg2}")
return func(*args, **kwargs)
return wrapped_func
@MyDecorator("Hello", "World")
def my_function():
print("Function is called")
my_function()
my_function()
# Arguments received: Hello, World
# Function is called
# Arguments received: Hello, World
# Function is called
상속
class Car:
...
class Truck(Car):
...
클래스정의시 상속할 클래스명을 삽입하여 진행한다.
메서드 오버라이딩 개념역시 적용된다.
메서드해석순서(MRO:method resolution order) - 다중상속
class MyClass:
pass
if __name__ == '__main__':
print(MyClass.mro()) # [<class '__main__.MyClass'>, <class 'object'>]
print(type(MyClass.mro())) # <class 'list'>
python
은 MRO
라는 클래스간 상속트리(함수호출순서)를 구축한다.
class Parent:
pass
class Child(Parent):
pass
if __name__ == '__main__':
print(Child.mro()) # [<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>]
MRO
의 출력결과를 보면 배열안에 Child, Parent, object
가 순서대로 들어가 있다.
이번엔 다중상속시에 MRO
구성을 확인해보면 C, A, B, object
순서대로 들어가 있다.
class A(object):
def foo(self):
pass
class B(object):
def bar(self):
pass
class C(A, B):
def baz(self):
pass
if __name__ == '__main__':
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
메서드가 호출되면 MRO 상속트리를 기준으로 호출할 메서드를 찾는다.
클래스정의문 또한 일종의 참조변수에 존재하는 데이터(참조변수)로써 클래스관련 동작을 할 때 마다 해당 참조변수로부터 값을 가져와 동작시킨다.
그래서 아래같은 클래스정의 참조변수를 사용한 이상한 코드 역시 정상 동작한다.
def test_func():
return Parent # 파이썬 가장 기본클래스 를 가리키는 참조변수
class Parent:
pass
class Child(test_func()):
pass
if __name__ == '__main__':
print(Child.mro()) # [<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>]
super
super
에는 2개의 인자값이 들어간다.
- 첫번째 인자는 프록시로 생성할 클래스타입
- 두번째 인자는 인스턴스, 첫째 인자의 자식 인스턴스이어야함.
파이썬에서 super
는 상위객체의 생성자를 호출하는 것과 비슷하다.
super
를 호출할 때 마다 첫번째 인자의 프록시 객체 super<상위객체>
가 생성된다.
아래와 같이 바로 상위객체의 함수를 호출하기 위해 super(C, self)
이런식으로 많이 사용했는데
Python 3
부터 super
함수의 인자가 사라졌다, 바로 위의 부모 프록시 객체를 생성한다는 가정 하에 super().foo()
형태로 호출 가능하다.
class A(object):
def foo(self):
return "foo"
class B(object):
def foo(self):
return "bar"
def bar(self):
return "bar"
class C(A, B):
def foo(self):
return "baz" + super(C, self).foo() + super(A, self).foo()
def bar(self):
return "baz" + super().bar()
if __name__ == '__main__':
c = C()
print(C.mro()) # [<class '__main__.C'>,
# <class '__main__.A'>,
# <class '__main__.B'>,
# <class 'object'>]
print(c.foo()) # bazfoobar
print(c.bar()) # bazbar
MRO
리스트를 살펴보고 super
의 첫번째 인자를 삽입하여 상위클래스의 메서드를 호출시킬 수 있다.
Enum
from enum import Enum, IntEnum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
class Status(IntEnum):
SUCCESS = 1
FAILURE = 2
PENDING = 3
if __name__ == '__main__':
for color in Color:
print(color)
# Color.RED
# Color.GREEN
# Color.BLUE
@dataclass
python 3.7 에 추가
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
자동으로 __init__, __repr__, __eq__
등의 메서드를 생성한다.