객체지향 (1)
1. 파이썬 객체지향의 특징
- object: 파이썬의 최상위 객체
- 접근제한자가 없음
2. 클래스 생성 / 인스턴스화
클래스 생성
class A:
a = 1 # class attribute
인스턴스화
- 사용하기 위해서는 반드시 인스턴스화 시켜야 한다.
A.a
를 해서도 사용할 수 있지만 인스턴스화한 것은 아님- 파이썬은 클래스 자체가 인스턴스된 것이기 때문
# 인스턴스화 하기
aa = A()
3. class attribute / instance attribute
(1) class attribute
- 클래스가 접근할 수 있는 것
- dir(클래스이름) 했을 때 나타남
- 만든 클래스 어트리뷰트 외의
__...__
들은 최상위 객체인 object를 상속받기 때문에 생기는 것
- 만든 클래스 어트리뷰트 외의
- 클래스 어트리뷰트가 아닌 것을
A.b
와 같이 부르려고 하면 Attribute Error 발생
(2) instance attribute
- 인스턴스는 자신의 고유한 값/메서드를 가질 수 있음
- instance attribute: 인스턴스의 고유한 값
- 값의 추가/삭제가 자유로움
- 인스턴스 어트리뷰트를 따로 만들지 않으면
A.a
와 같이 불렀을 때 클래스 어트리뷰트를 찾아서 반환함.
aa.a = 3 # instance attribute
실제로 인스턴스 어트리뷰트는 이렇게 만들 것이 아니라 인스턴스 메서드 안에 만드는 것이 좋다.
4-(2) instance method
4. class method / instance method
(1) class method
class A:
a = 1
b = 2
def __init__(self):
self.a = 3
print('init')
@classmethod
def bb(cls): # 관용적으로 cls라고 씀
cls.b = 4
print('bb')
A.bb() # bb
aaa = A()
aaa.bb() # bb
(2) instance method
- 인스턴스 어트리뷰트는 인스턴스 메서드 안에 만들어야 한다.
def __init__(self)
: 인스턴스화 할 때 실행
class A:
a = 1
b = 2
def __init__(self):
self.a = 3 # 실행하는 순간 self 대신 aaa가 들어감
print('__init__')
def bb(self):
self.b = 4
aaa = A()
aaa.a # 3
vars(aaa) # {'a': 3}
__init__
외의 메서드 안에 생성한 어트리뷰트는 해당 메서드를 실행해야 인스턴스 어트리뷰트로 사용할 수 있다.- 즉, 실행하는 시점에 따라 인스턴스 어트리뷰트가 달라진다.
vars()
를 이용해 체크한다.
aaa.b # 2
aaa.bb()
aaa.b # 4
vars(aaa) # {'a': 3, 'b': 4}
5. 객체지향 관련 명렁어
(1) instance attribute 확인 명령어
vars(aaa) # {'a': 3}
aaa.__dict__ # {'a': 3}
(2) 부모 확인 명령어
type(aaa) # __main__.A
aaa.__class__ # __main__.A
6. 리터럴 방식 vs. 인스턴스화 방식
# 리터럴 방식
a = 1
b = ['홍', '길', '동']
# 인스턴스화 방식
a = int(1)
b = list('홍길동')
7. 상속
파이썬은 다중상속을 지원하며, 똑같은 이름이지만 행동을 다르게 하는 다형성
을 지원한다.
(1) 상속 기본형태
부모 역할인 클래스명(T)는 괄호를 생략해서 쓸 수 있고 기본적으로 파이썬의 최상위 객체인 object를 상속한다.
class T:
x = 1
class S(T):
pass
class K(T):
x = 2 # overriding
S, K는 각각 T를 상속
했으므로 T의 x를 사용할 수 있다. ( dir()로 확인 가능 )- K는
오바라이딩
을 하였으므로K.x
로 호출하면 새로 설정된 2가 나오게 된다.
(2) 상속 순서
똑같은 메서드일 때는 어떤 것을 먼저 실행할지 순서를 정할 수 없다 (consistent MRO를 만들 수 없다) 라고 TypeError가 난다.
MRO : Method Resolution Error
class NO(T, K):
pass
# TypeError
상속 순서를 확인하기 위해 __mro__
를 사용할 수 있다.
- 단, 상속은 클래스 기준이기 때문에
__mro__
는 클래스에만 사용할 수 있다. __mro__
는 dir() 했을 때는 보이지 않아도 사용할 수는 있다.__mro__
대신mro()
도 사용 가능하다. 전자는 튜플로 결과값을 반환하고 후자는 리스트로 반환한다.
K.__mro__ # (__main__.K, __main__.T, object)
kk = K()
kk.__mro__ # AttributeError
(3) 다이아몬드 구조
class A:
def __init__(self):
self.x = 'A'
print('A')
class B(A):
def __init__(self):
self.x = 'B'
print('B')
class C(A):
def __init__(self):
self.x = 'C'
print('C')
class D(B,C):
def __init__(self):
print('D')
d = D() # D
d.x # AttributeError
상속을 하더라도 실행을 하지 않으면 값이 전달되지 않고, x값이 안나온다.
A.__init__(self)
으로 실행시켜줘야 한다.
class A:
def __init__(self):
self.x = 'A'
print('A')
class B(A):
def __init__(self):
self.x = 'B'
A.__init__(self)
print('B')
class C(A):
def __init__(self):
self.x = 'C'
A.__init__(self)
print('C')
class D(B,C):
def __init__(self):
C.__init__(self) # 실행시켜줘야함
B.__init__(self)
print('D')
d = D() # ACABD
d.x # 'A'
여러번 상속되면서 생기는 문제는 super().__init__()
을 사용해 상속체계를 내부적으로 정리하게 하여 해결한다.
- super: 부모클래스의 객체를 대표하는 인스턴스로,
중복 문제를 해결
할 수 있다.
class A:
def __init__(self):
self.x = 'A'
print('A')
class B(A):
def __init__(self):
self.x = 'B'
super().__init__() # 상속체계 내부적으로 정리
print('B')
class C(A):
def __init__(self):
self.x = 'C'
super().__init__()
print('C')
class D(B,C):
def __init__(self):
super().__init__()
print('D')
d = D() # ACBD
D.mro() # [__main__.D, __main__.B, __main__.C, __main__.A, object]
Leave a comment