클래스
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 32
| 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) c1.print_name()
Truck.brand = 'kia' t1 = Truck('bongo', 10) print(Truck.brand) t1.print_name() t1.print_size() print(Car.brand)
|
클래스변수는 클래스 단위에서 공유되는 전역변수
Car.brand, Truck.brand 을 출력값을 확인하면 알 수 있다, 초기값은 부모클래스에서 가져온다.
메서드
아래 2종류의 메서드에 대해 알아보자.
- 정적메서드
- 클래스메서드
클레스메서드는 좀더 기능이 추가된 정적메서드라 볼 수 있다.
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
| 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) print(rad)
|
클래스메서드 특징
- 객체생성 없이 바로 호출 가능
cls 클래스바인딩을 사용해 클래스 상태(변수)에 접근가능
- 주로 펙토리 패턴으로 객채생성 해야할 경우 사용함
1 2 3 4
| @classmethod def factory(cls, init_dict): return cls(init_dict.name, init_dict.age)
|
special method
__init__, __call__
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class CountCalls(object): def __init__(self, num1, num2): self.num1 = num1 self.num2 = num2 print("init invoked", self.num1, self.num2) def __call__(self, keyword1, keyword2): print("call invoked", keyword1, keyword2)
if __name__ == '__main__': cc1 = CountCalls(1, 2) cc1("test", "print")
|
__call__ 은 인스턴스를 함수처럼 사용할 수 있게 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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))
add_ten = Adder(10) print(add_ten(10))
|
__str__, __repr__
__str__: str은 입력 받은 객체의 문자열 버전을 반환하는 함수
__repr__: Representation 의 약자,
추상메서드
추상메서드는 하위 클래스가 재정의하지 않으면 오류를 발생하기 위한 개념으로 자주 사용한다.
JIT 언어인 파이썬에서 아래와 같이 Pizza 객체를 생성했다고 컴파일에러를 발생시키지 않기에 쉽게 버그가 발생한다.
1 2 3 4 5 6 7 8 9
| class Pizza(object): pass
class CheesePizza(Pizza): def get_name(self): return "cheese"
if __name__ == '__main__': p = Pizza()
|
이때 유용하게 사용할 수 있는 패키지가 abc(abstract base class)
1 2 3 4 5 6 7 8 9 10 11 12
| from abc import ABC, abstractmethod
class Pizza(ABC): @abstractmethod def get_name(self): """TODO method overriding"""
if __name__ == '__main__': p = Pizza()
|
클래스 decorator
__call__ 를 사용하면 클래스를 함수처럼 사용할 수 있기 때문에 클래스를 decorator 로 정의할 수 있다.
아래 같이 클로저를 사용하지 않아도 내부 변수를 유지하기 위해 사용할 수 있고
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 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) print(print_hello2.called)
|
decorator 매개변수 지정하는 것도 중첩함수 정의 없이 쉽게 정의 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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()
|
상속
1 2 3 4 5
| class Car: ...
class Truck(Car): ...
|
클래스정의시 상속할 클래스명을 삽입하여 진행한다.
메서드 오버라이딩 개념역시 적용된다.
메서드해석순서(MRO:method resolution order) - 다중상속
1 2 3 4 5 6 7
| class MyClass: pass
if __name__ == '__main__': print(MyClass.mro()) print(type(MyClass.mro()))
|
python 은 MRO 라는 클래스간 상속트리(함수호출순서)를 구축한다.
1 2 3 4 5 6 7 8 9 10
| class Parent: pass
class Child(Parent): pass
if __name__ == '__main__': print(Child.mro())
|
MRO 의 출력결과를 보면 배열안에 Child, Parent, object 가 순서대로 들어가 있다.
이번엔 다중상속시에 MRO 구성을 확인해보면 C, A, B, object 순서대로 들어가 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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())
|
메서드가 호출되면 MRO 상속트리를 기준으로 호출할 메서드를 찾는다.
클래스정의문 또한 일종의 참조변수에 존재하는 데이터(참조변수)로써 클래스관련 동작을 할 때 마다 해당 참조변수로부터 값을 가져와 동작시킨다.
그래서 아래같은 클래스정의 참조변수를 사용한 이상한 코드 역시 정상 동작한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def test_func(): return Parent
class Parent: pass
class Child(test_func()): pass
if __name__ == '__main__': print(Child.mro())
|
super
super 에는 2개의 인자값이 들어간다.
- 첫번째 인자는 프록시로 생성할 클래스타입
- 두번째 인자는 인스턴스, 첫째 인자의 자식 인스턴스이어야함.
파이썬에서 super는 상위객체의 생성자를 호출하는 것과 비슷하다.
super 를 호출할 때 마다 첫번째 인자의 프록시 객체 super<상위객체> 가 생성된다.
아래와 같이 바로 상위객체의 함수를 호출하기 위해 super(C, self) 이런식으로 많이 사용했는데
Python 3 부터 super 함수의 인자가 사라졌다, 바로 위의 부모 프록시 객체를 생성한다는 가정 하에 super().foo() 형태로 호출 가능하다.
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
| 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())
print(c.foo()) print(c.bar())
|
MRO 리스트를 살펴보고 super 의 첫번째 인자를 삽입하여 상위클래스의 메서드를 호출시킬 수 있다.
Enum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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)
|
@dataclass
python 3.7 에 추가
1 2 3 4 5 6
| from dataclasses import dataclass
@dataclass class Person: name: str age: int
|
자동으로 __init__, __repr__, __eq__ 등의 메서드를 생성한다.