객체지향 (2)
1. 상속 - 부모 찾기
부모를 찾는 명령어
ex. C가 A, B를 다중상속받았을 때
issubclass(C, A)
: True 반환C.__base__
: A만 알려줌.C.__bases__
: A, B를 알려줌C.mro()
기타 명령어 정리
is
: 메모리까지 같은지 비교. Boolean 반환isisntacne
: 인스턴스인지 확인
c = C()
isinstance(c, C) # True
isistance(1, int) # True
2. 상속 - 오버라이딩
상속받은 것 외의 새로운 기능을 추가할 수도 있고, 상속받은 기능을 변경할 수도 있다.
class X:
def xx(self):
print('X')
class Y(X):
def xx(self):
print('Y')
y = Y()
y.xx() # Y
3. Delegation (위임)
위임의 매커니즘 1 : 상속
- 파이썬에서는 상속이 위임으로 돌아간다.
- 암시적으로 부모에게 일을 위임한다.
- 적은 코드로 깔끔하게 수행할 수 있다.
- 속도를 희생하지만 메모리 차지를 덜할 수 있다.
class A:
def xx(self):
x = 1
print('xx')
class B(A):
pass
A.xx # <function __main__.A.xx(self)>
B.xx # <function __main__.A.xx(self)>
# 일은 A에서 함
class X:
def xx(self):
print('X')
class Y(X):
def xx(self):
print('----')
X.xx(self)
y = Y()
y.xx()
# ----
# X
다중상속일 때 겹치는 부분을 제외하고 싶다면 X.xx(self)
대신 super().xx()
나 super(Y, self).xx()
를 사용한다.
super().xx()
: 파이썬 3부터 지원하는 방식super(Y, self).xx()
: 파이썬 2 방식
위임의 매커니즘 2 : Composition (컴포지션)
- 상속을 사용하지 않고 원하는 기능을 가져오는 것으로, 실제 객체를 인스턴스하여 사용한다.
- 명시적으로 일을 위임한다.
- 상속보다 복잡하다.
- 사용 이유: 디커플링 (강한 결합을 피할 수 있음)
- 즉, 변경하고자 하는 기능이 많거나 너무 많이 유기적으로 연결되어 있을 때는 상속보다 컴포지션이 유연하다.
class Door:
colour = 'brown'
def __init__(self, number, status):
self.number = number
self.status = status
def open(self):
self.status = 'open'
def close(self):
self.status = 'closed'
class SecurityDoor:
colour = 'gray'
locked = True
def __init__(self, number, status):
# Door 객체 인스턴스화
self.door = Door(number, status)
def open(self):
if self.locked:
return
self.door.open()
def close(self):
self.door.close()
__getattr__
class A:
def __init__(self, x):
self.x = x
def __getattr__(self, t): # 호출한 attriute가 없으면 실행
print('__getattr__ 실행: ',t)
a = A(3)
a.x # 3
# __getattr__ 없을 때는 AttributeError 발생
a.y # __getattr__ 실행: y
class SecurityDoor:
locked = True
def __init__(self, number, status):
self.door = Door(number, status)
def open(self):
if self.locked:
return
self.door.open()
def __getattr__(self, attr):
return getattr(self.door, attr)
s = SecurityDoor(5, 'default')
# __getattr__ 없을 때는 아래 코드를 실행하면 AttributeError 발생
s.status # default
__getattr__
!=getattr()
==__getattribute__
class S: x = 2 def __getattribute__(self, a): print('getattribute:',a) s = S() s.x # getattribute: x getattr(s, 'x') # getattribute: x
4. Descriptor (디스크립터)
.
의 행동을 바꾸는 것을 디스크립터라고 한다.
디스크립터를 만드는 3가지 방식
- composition 방식
- property 방식
- decorator 방식
(1) composition 방식
__get__
, __set__
, __del__
를 사용한다.
class Value:
def __init__(self, x):
print('init')
self.x = x
def __get__(self, a, b):
print('get')
return self.x
def __set__(self, x):
print('set')
self.x = x
# Myclass에서 Value를 초기화
class Myclass:
t = Value(1)
# get 호출
Myclass.t
# get
# 1
(2) property 방식
사용자가 임의로 값을 추가 / 삭제하는 것을 방지할 수 있다.
class C:
def __init__(self, x):
self.__x = x
def getx(self):
return self.__x
def setx(self, value):
self.__x = value
def delx(self):
del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
c = C(4)
# get 호출
c.x # 4
# set 호출
c.x = 3
# del 호출
del c.x
Mangling
- ex.
__a
- __가 붙으면 호출할 때 사용되어야 하는 이름을 바꿀 수 있다.
- 중요한 정보를 감출 수 있어 private 기능으로 활용할 수 있다. (information hiding 기법)
(3) decorator 방식
@property
를 사용
- 파이썬은 오버로딩을 지원하지 않으므로, 이름이 같은 메서드가 있다면 나중에 실행된 메서드가 이전에 실행된 메서드를 덮어쓰게 된다.
- 하지만, 해당 데코레이터를 사용하면 내부적으로 다르게 처리하게 만들기 때문에 이름이 같아도 덮어쓰지 않는다.
class D:
def __init__(self, x):
self.__x = x
@property # 해당 데코레이터가 callable -> not callable로 변하게 함
def x(self):
print('xxx')
return self.__x
@x.setter
def x(self, x):
print('set')
self.__x = x
d = D(3)
d.x
# xxx
# 3
d.x = 5
# set
5. _
용법 정리
- 맨 마지막 output 값 반환
- 숫자 사이에 들어가는
_
100_000
가 의미하는 것은 100,000이다.
- import * 했을 때 임포트 안되는 것 만들 때
- Mangling
- 뒤섞인 형태로 변형
-
class T: __x = 1 T.__x # AttributeError T._T__a # 1
- dunder = magic method = special method
- ex.
__init__
- ex.
- 이름 같은 것을 쓸 수 없지만 유사한 기능을 제공하고자 할 때
- ex.
isin_
- ex.
6. Metaclasses
- 클래스의 클래스로, 클래스의 행동을 제약한다.
- 메타클래스의 인스턴스는 클래스이다.
- type을 상속받으면 메타클래스
- type(오브젝트)는 클래스 이름을, type(클래스)는 type(메타클래스)를 반환한다.
# 클래스 만들기
t = type('int2', (int,), {})
issubclass(t, int) # True
지금까지 __base__
, __bases__
, __mro__
같은 것들은 dir()로 보이지 않았다. 바로 메타클래스에서 관리하는 것들이기 때문이었고, 나오게 하기 위해서는 dir(클래스명.__class__)
와 같이 사용한다.
Leave a comment