[TIL] Python Composite pattern(composition), Descriptor, Meta class

Hanna·2021년 9월 9일
1

Python

목록 보기
1/4

Composite pattern의 활용

composite pattern 중 하나의 속성은 inheritance를 대신 할 수 있음


class A:

class C:

class B:
    def __init__(self):
        self.a = A()
        self.c = B()
    
    def multi(self):
        return self.a * self.b

class AA:
   a = 1
   def aaaa(slef, x):
       self.b = x
   def aaa(self):
       return self.b

class BB(AA):
    pass

상속의 문제점 : AA와 coupling. 밀접하게 연결이 되어 있어서, class AA가 바뀌면 class BB의 기능도 바뀌어짐.

class CC: ## AA가 변할 때 같이 안 변함
    a = 1 # overiding처럼 작동함
    def __init__(self):
        self.y = AA()
    def aa(self, x):
        self.y.b = x
    def aaa(self):
        return self.y.b
        
class DD: ## AA가 변할 때 같이 안 변함
    a = 1 # overiding처럼 작동함
    def __init__(self):
        self.y = AA()
    def aa(self, x):
        self.y.aa(x)
    def aaa(self):
        return self.y.aaa()

파이썬의 inheritance는 delegate
BB.a가 없으면 AA것을 찾음(delegate) -> BB를 overiding 하지 않은 상태에서 AA가 값이 바뀌어버리면 값이 그대로 들고 옴. self.y.b는 인스턴스이기 때문에 변하지 않음

인스턴스가 수십개 있을 경우, 수정해야 하는 것들이 넘쳐나기 때문에 composition 방식을 사용

class EE:
    a = 1
    def __init__(self):
        self.y = Aa()
    def aa(self): # overriding 처럼 사용 가능
        print('aa')
    def __getattr__(self, x): # try: attribute arror, attribute를 이름으로 반환함
        return getattr(self.y, x) 

참조가 안 될 때 attribute 에러가 발생함. 그런데 __getattr__가 정의되어 있고, 인스턴스에 attribute error가 발생하면 __getattr__가 실행 됨.

a = [1, 2, 3]
getattr(a, 'pop')() #attribute는 이름으로 반환
>> 3
class EE:
    a = 1
    def __init__(self):
        self.y = Aa()
    def aa(self): # overriding 처럼 사용 가능
        print('aa')
    def __getattribute__(self, x): # .에 대해 어떻게 처리 해야 할지 알려주는 것
        return getattr(self.y, x) 

_ (underbar)의 의미

  1. _name 의미 : 내부적으로만 사용
    1. from om X import *할 때 딸려오지 않는다.
    2. 자동완성이 안 됨
      => 내부적으로만 사용하겠다고 암시
  2. _name : i18n (internationalization)
  3. name : python에 있는 것과 충돌 방지용 (관례)
    np.int
    : numpy에서 재정의한 int
  4. _ : 사용하지 않을 변수 (관례)
  5. __name : mangling(private 비슷하게 사용), 이름이 변함
class X:
    __a = 1
print(X._x__a)
>> 1
  1. : interactive last result, 변수 이전 마지막 result를 가지고 옴
  2. __x__ : double underbar (dundu / magic(special) method)

cf. sklearn에서는 dir(iris) 결과에 _가 없는 이유

__all__ = [a, _a]  # * 로 import 할 때 지정할 수 있음
__slot__ # 접근하지 못하게 할 수 있음

Descriptor

g.a = 1 # setter in oop
g.a 	# getter
del g.a # deleter

setter, getter, deleter할 때 조작을 할 수 있는 것 -> descriptor
descriptor를 통해 사용자들이 값을 사용 못하게 하거나, 값을 숨길 수 있음

Descriptor를 만드는 방법

# 1
class Name:
    def __init__(self):
        self._name = ''
    def __get__(self, instance, owner):
        return self.name
    def __set__(self, instance, name):
        self.name = name
    def __delete__(self, instance):
        del self._name

class Person:
    name = Name()

p = Person() 

# 2
class Person2:
    def __init__(self):
        self._name = ''
    def fget(self):
        return self.name
    def fset(self, name):
        self.name = name
    def fdelete(self):
        del self._name
    name = property(fget, fset, fdel)

p2 = Person2()

#3
class Person3:
    def __init__(self):
        self._name = ''
    @property
    def name(self):
        return self.name
        
    @property.setter
    def name(self, name):
        self._name = name
        
    @property.deleter
    def name(self):
        del self.name

p3 = Person3()
   
  1. else : if else, for/while else, try else
  2. from : from import, raise from, yield from
  3. as : import as, except as, with as

Metaclass

원래 instance를 무제한으로 쓸 수 있으나 1개의 instance만 만들 수 있게 class의 기능을 변경
meta class : class의 class. class의 기능을 바꿀 수 있음

a = 0
type(a)
>> int
type(int)
>> type

type 자체가 class의 class, 즉 meta class임

class Singleton(type): # metaclass를 상속해서 metaclass를 만듦
    _instance = None
    def __call__(cls):
        if cls ._instance is None:
            cls._instance = super().__call__()
        return cls._instance

class Myclass(metaclass=Singleton):
    pass

m = Myclass()
s = Myclass()
m.a = 1
s.a
>> 1
s.bb = 3
m.bb
>> 3
k = Myclass()
k.bb
>> 3
s is k
>> True

class의 instance가 None이면, 새로 만들고 None이 아니면 기존의 클래스를 불러와라 -> instance를 1개만 만들도록 class의 기능을 변경함
meta class : class의 기능을 변경하는 것

composition 방식 다른 class의 instance를 안에 집어넣어서 행동하게 하는 것 composition 방식을 활용하여 상속을 대신하는 기법도 있고, descriptor를 만들 수도 있음

composition 방식에 다양한 활용이 있는 것 처럼, meta class의 활용도 다양함
머신러닝에서는 abstract base class를 주로 사용함.

abstract란 규칙을 따른 다는 것. 공통되는 규칙을 따라서, 차이점만 새로 구현하도록 하는 방식.
예로, sklearn 패키지의 Navie_bayes에 변형들이 많음. 공통적인 부분만 뽑아놓고, 차별적인 것만 구현하도록 하면 사용자들은 규칙만 따른다면, 각각의 Navie_bayes 알고리즘을 만들 수 있음

from skleran.navie_bayes import BernoulliNB, CategoricalNB, MultinomialNB

python에서 제공하는 class의 기능을 모두 바꿀 수 있음 -> 확장성
meta class 기능 중 abstract base class는 공통 기능을 뽑아놓고, 기능을 강제 시킴

#ABCMeta는 abstract base class 형태로 기능을 바꿈(meta clasS), abstractmethod는 기능을 강제적으로 구현하게 하는 method
from abc import ABCMeta, abstractmethod 
class A(metaclass=ABCMeta):
    @abstractmethod
    def a(self):
        pass

class B(A):
    def a(self):
    	print('a')
    
b = B()

B에서 abstractmethod를 구현하지 않으면 ImplementedError 발생함

class S(tf.keras.utils.Sequence): # duck-typing 방식으로 meta class를 받음
    pass
profile
매일 성장하고 있습니다

0개의 댓글