-
객체 지향 프로그래밍
-
각각의 객체(object) 는 역할, 책임 을 가지고 서로 협력 하여 동작한다.
-
책임(역할): 각 객체는 적절한 역할을 수행 할 책임이 부여된다.
-
협력적: 요청에 대해서 응답을 해주는 역할(책임) 을 갖고 있다.
- 객체간 의사소통은 단 한가지 message 로만 가능하다.
-
자율적: 요청의 처리에 대해서는 마음대로 한다.
- 객체의 메소드가 수신된 메세지를 처리한다.
-
-
왜 객체지향 프로그래밍을 할까
- 관리/수정 이 편하다
- 프로그램 확장이 편하다
- 객체를 재사용 할 수 있다.
-
일반적으로 class = object 를 동일시 하는 경향이 있다.
- 하지만, class 는 객체를 코드로 옮기는 도구 이다.
-
object(객체) 와 instance(인스턴스)
- class
- object 를 코드로 표현하기 위한 도구
- object(객체)
- class 를 이용해서 객체의 모양을 구현한 것
- instance(인스턴스)
- class 로 구현된 object 가 실제 메모리영역(힙영역) 에 할당 된 것
- class
# new/old style 구분은 python 2.2 부터 구분
# python 3.x 부터는 내부적으로 무조건 object 를 자동으로 상속, 아래모든 내용이 동일하다.
class MyClass(object):
pass
class MyClass():
pass
class MyClass:
pass- 메모리를 할당하여 값을 초기화 해주는 것
__init__- 생성자라고 하기에는 애매한 점이 있다.
- 클래스 object 에 메모리를 할당하지 않기 때문
__init__메소드는 속성 값을 초기화 해주는 일을 한다.- initializer
- 생성자라고 하기에는 애매한 점이 있다.
__new__- 클래스 object 를 생성(메모리 할당) 을 하고 반환한다.
- 하지만 메모리 할당 만으로 생성자라고는 하지 않는다.
- instantiator(인스턴시에이터)
- 클래스 object 를 생성(메모리 할당) 을 하고 반환한다.
- 즉, 파이썬에는
__new__,__init__메소드가 동작하여 객체가 생성된다. - 예제코드
class MyClass(object):
def __new__(cls, *args, **kwargs):
print('__new__ is called')
return super().__new__(cls, *args, **kwargs)
def __init__(self):
print('__init__ is called')
self.alpha = 1
print('== start ==')
mc = MyClass()
'''
== start ==
__new__ is called
__init__ is called
'''- python 은
__del__이라는 소멸자가 미리 정의 되어있다. - 이 메소드의 내용은 개발자가 변경이 불가능하다.
del로 인스턴스를 삭제 할때__del__이 수행된다.
class MyClass(object):
def __new__(cls, *args, **kwargs):
print('__new__ is called')
return super().__new__(cls, *args, **kwargs)
def __init__(self):
print('__init__ is called')
self.alpha = 1
def __del__(self):
print('__del__ is called')
print('== start ==')
mc = MyClass()
del mc
'''
== start ==
__new__ is called
__init__ is called
__del__ is called
'''- 싱글톤: 처음 인스턴스 생성시 메모리에 할당 해놓고, 다음 부터는 생성된 메모리를 참조해서 같은 인스턴스를 가져오는 방법
class SingletonClass(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
print('__new__ is first called')
cls._instance = super().__new__(cls)
else:
print('__new__ is second called')
return cls._instance
def __init__(self, alpha: int):
self.alpha = alpha
print('== start ==')
a = SingletonClass(1)
print(a, a.alpha)
b = SingletonClass(2)
print(b, b.alpha)
print(a, a.alpha)
'''
== start ==
__new__ is first called
<__main__.SingletonClass object at 0x103ccd7c0> 1
__new__ is second called
<__main__.SingletonClass object at 0x103ccd7c0> 2
<__main__.SingletonClass object at 0x103ccd7c0> 2
'''-
python 은 모든 것이 객체다.
- class 또한 객체다 ->
class SampleClass: pass로 코드로 선언된 것은 메타클래스 의 객체다
- class 또한 객체다 ->
-
메타클래스는 일반적으로 사용하는 일은 별로없다. -> 프레임워크 개발에 사용되는 정도...
-
메타클래스 =
type()- type 함수는 일반적으로 변수가 어떤 타입인지 체크하는 용도로 사용한다.
-
메타클래스로 할 수 있는게 뭔가?
- type 함수를 이용해서 동적으로 클래스를 만들 수 있다.
a = type('MyClass', (object,), {'a': 3, 'm': lambda x, y: x + y}) # type(클래스명, 상속클래스, 속성/메소드) print(a) print(a.a) print(a.m(3, 4)) ''' <class '__main__.MyClass'> 3 7 '''
- 커스텀 메타클래스를 만들 수 있다.
- 클래스를 컨트롤 하여 원하는 방향으로 생성하도록 만들 수 있다.
- 예제) 특정 속성(a) 가 무조건 정수가 되도록 하는 메타클래스
class MyMetaClass(type): def __new__(mcs, cls_name, bases, namespace): assert type(namespace['a']) is int, 'the a is not integer' return type.__new__(mcs, cls_name, bases, namespace) class MyClass(metaclass=MyMetaClass): a = 3.14 a = MyClass() ''' AssertionError: the a is not integer '''
부모 클래스(상위 클래스)의 속성, 메소드 를자식 클래스(하위 클래스)가 상속 받는 것
class Person(object):
def __init__(self, name: str, age: int, gender: str):
self.name = name
self.age = age
self.gender = gender
def introduction(self):
print(f'name: {self.name}, age: {self.age}, gender: {self.gender}')
class Teacher(Person):
pass
t = Teacher(name='aaa', age=40, gender='man')
t.introduction()
'''
name: aaa, age: 40, gender: man
'''class Teacher(Person):
def __init__(self, position: str, *args, **kwargs):
super(Teacher, self).__init__(*args, **kwargs)
self.position = position
def my_position(self):
print(f'my position is : {self.position}')
t = Teacher(name='aaa', age=40, gender='man', position='1반 선생님')
t.introduction()
t.my_position()
'''
name: aaa, age: 40, gender: man
my position is : 1반 선생님
'''- 부모 클래스의 메소드를 자식 클래스에서 재정의 하여 사용하는 것
class Teacher(Person):
def __init__(self, position: str, *args, **kwargs):
super(Teacher, self).__init__(*args, **kwargs)
self.position = position
def introduction(self):
print("I'm teacher.")
t = Teacher(name='aaa', age=40, gender='man', position='1반 선생님')
t.introduction()
'''
I'm teacher.
'''- 여러 부모를 상속 받는 것
- 예제
- 단순히 상위 클래스의 init 을 호출
- 같은 상위 클래스를 두번 호출하는 경우가 생김
class A(object):
def __init__(self):
print('A class')
class B(A):
def __init__(self):
print('B class')
A.__init__(self)
class C(A):
def __init__(self):
print('C class')
A.__init__(self)
class D(B, C):
def __init__(self):
print('D class')
B.__init__(self)
C.__init__(self)
d = D()
'''
D class
B class
A class
C class
A class
'''- 위 문제를 해결하기 위해서
super를 사용한다.
class A(object):
def __init__(self):
print('A class')
class B(A):
def __init__(self):
print('B class')
super().__init__()
class C(A):
def __init__(self):
print('C class')
super().__init__()
class D(B, C):
def __init__(self):
print('D class')
super().__init__()
d = D()
'''
D class
B class
C class
A class
'''- class 의 행위를 담당하는 함수
- instance 가 생성된 후 사용되는 것으로, 첫번째 인자로
self(자기자신 instance) 를 넘겨 받는다 self파라미터를 통해서 자신(instance)의 (state)상태값을 변경 할 수 있다.- 또한
self.__class__를 통해서 class 의 state(상태) 도 변경가능하다.
class A(object):
cnt = 0
def instance_method(self, b: int):
self.cnt += b
a = A()
print(f"A class's cnt: [{A.cnt}], a instance's cnt : [{a.cnt}]")
a.instance_method(3)
print(f"A class's cnt: [{A.cnt}], a instance's cnt : [{a.cnt}]")
'''
A class's cnt: [0], a instance's cnt : [0]
A class's cnt: [0], a instance's cnt : [3]
'''@classmethod데코레이터를 사용한다.- class (not instance) 의 state 를 접근 할 수 있다.
- 첫 번째 인자로
cls(자기 자신 class object) 를 넘겨 받는다. - 각 instance 를 통해서도 접근가능하다.
class B(object):
cnt = 0
@classmethod
def class_method(cls, b: int):
cls.cnt += b
b = B()
print(f"B class's cnt: [{B.cnt}], b instance's cnt : [{b.cnt}]")
B.class_method(3)
print(f"B class's cnt: [{B.cnt}], b instance's cnt : [{b.cnt}]")
'''
B class's cnt: [0], b instance's cnt : [0]
B class's cnt: [3], b instance's cnt : [3]
'''@staticmethod데코레이터를 사용한다.self나cls인자를 넘겨 받지 않는다. -> 일반적인 함수와 비슷하게 사용된다. (상태 저장을 하지 않는다.)- staticmethod 를 굳이 사용하는 이유는 비슷한 함수를 하나의 namespace 에 묶기 위해서 사용되는 정도...?
class C(object):
cnt = 0
@staticmethod
def static_method(x: int, y: int):
return x + y
print(C.static_method(1, 3))
'''
4
'''- double-under method = dunder method = magic method
- under-bar(
_) 두개로 시작 해서_두개로 끝나는 메소드 - 파이썬 빌트인 메소드를 사용자가 커스텀하게 사용하기 위한 방법
- 예를 들어,
a + b를 하면a.__add__(b)가 호출 된다. __repr__,__str__로 객체를 문자열로 표현 할 수 있다.
- 예를 들어,
- 예제 1
- 새로 만든 object 를
+오퍼레이터로 더하기 하는 예제
- 새로 만든 object 를
class NewInt(object):
def __init__(self, value: int):
self.value = value
def __add__(self, other):
return self.value + other.value
new_int_a = NewInt(3)
new_int_b = NewInt(4)
print(f"더하기 결과 : {new_int_a + new_int_b}")
print(f"빼기 결과 : {new_int_a - new_int_b}") # 빼기는 구현하지 않아서 오류 발생
'''
더하기 결과 : 7
TypeError: unsupported operand type(s) for -: 'NewInt' and 'NewInt'
'''- 예제 2
- file
open과 같이 항상 작업이 끝나면close를 해야하는 경우가 있다. - 이때
with문법을 사용하면 자동으로 close 가 되는데 이것은 내부적으로__enter__와__exit__으로 구성되어 있다.
- file
class A(object):
pass
class B(object):
def __enter__(self):
print("__enter__ is called")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__ is called")
pass
# with A() as a: # with A 는 에러가 발생
# print(a)
with B() as b:
print(b)
'''
__enter__ is called
<__main__.B object at 0x10b26fca0>
__exit__ is called
'''
