본문 바로가기

노력/인공지능

8. 파이썬 개인공부 8 (클래스)

오픈소스 기반으로 개발함. 다들 고급기법을 써서 이해하지 못하면 사용을 할 수가 없다.


Ephiphany : 자신도 모르게 깨닫는 것.





class A:

  def quark(self):

    print('a')

    

class B:

  def quark(self):

    print('b')

    

def x(t):

  print(t.quark())

  

x(A())

x(B())


이렇게 A와 B를 상속하지 않으면서 같은 함수를 만들면서 다른 클래스로 선언할 수 있다. 이런 것을 덕타이핑이라고 한다.




Type 1. 상속


sequence 2. protocol을 따른다.


이터러블이라는 최상위 객체를 상속해서 사용할 수 있고, 덕타이핑을 제공한다.


이터러블이 아닌데도 같은 것을 할 수 있는 경우가 있다. 덕타이핑을 제공하기 때문에...




is.... : True, False 반환 -> predicate라고 한다.



__base__, __bases__ : 부모확인


issubclass() : 



isinstance(3, (int, float)) : True


type(3) == int : True


이렇게 클래스의 인스턴스인지 확인할 수 있는 방법이 있다.


두 개 다 사용할 수 있다.




set() - set() : 차집합.




a=5


vars(a) : 타입에러


왜냐하면 a에 __dict__가 정의되어 있지 않기 때문에 내부적으로 변환해서 나오지 못한다.




함수도 객체다.



type, dir, vars : 삼총사 - 내가 뭘 사용할 수 있는지 알 수 있다.



def echo(a=7, b=6):

    b = 5

    return a

    

echo.__default__  ->  에코의 기본값들이 나온다.


오버라이딩 가능 : 순수하게 객체니까 모든 객체들을 덮어쓸 수 있다.





class Room:

    def __init__(self, door):

        self.door = door

        

    def open(self):

        self.door.open()


    def close(self):

        self.door.close()


    def is_open(self):

        return self.door.is_open()

        

        

덕타이핑.


init의 door에 open(), close(), is_open()만 어떻게 있으면 어떤 type의 인스턴스이던 입력할 수 있으며, 에러가 발생하지 않는다.



상속, 컴포지션, 새로 짜는 것 다 가능하다.


상속은 무조건 다 부모를 가져오기 때문에 메모리가 비대해 질 수 있다.


필요한 기능만 따로 있다면 필요한 것만 가지고 오자.



덕타이핑은 실수할 확률이 있다.


하나의 클래스를 변경할 때, 덕타이핑 되어 있는 다른 클래스도 변경해야 한다. 그렇지 못한다면 실수할 확률이 높아진다.



hasattr(3, 'to_bytes') : 파이썬 동네에서 좋아하지 않는다. True, 이건 LBYR


EAFP : 용서를 구하는 것이 허락받는 것 보다 쉬운일이다. 그렇기 때문에 위의 방식이 별로 좋지 않은 방식이다.



try, catch 방식이 더 선호받는다.



if hasattr(someobj, 'open'):

    [...]

else:

    [...]

    

위의 방식은 미리 open이라는 함수가 있는지를 들여다보고 가는 것이다. 파이써닉하지 못한 방법이다.

    

try:

    someobj.open()

    [...]

except AttributeError:

    [...]

    


클래스 제한 : __ 클래스안에서 사용하면 접근제한자가 된다.

접근 제한 가능.



class Y:

    def __init__(self, x):

        self.x = x

        

    @property

    def x(self):

        return self.x

        

a = Y(3)



AttributeError : attribute를 set할 수 없다.


@property를 이용해서 .의 기능을 바꿀 수 있다. 이런것을 디스크립터라고 부른다.



a.get_x : 3 괄호를 사용하지 않고 쓸 수 있게 된다.





class Y:

    def __init__(self, x):

        self.x = x

        

    @property

    def get_x(self):

        return self.x

    

class X:

    def __init__(self, x):

        self.x = x

        

    def get_x(self):

        return self.x

        

property가 붙은 것과 property가 붙지 않은 것과는 변한 것이 없다.


a = Y(1)

b = X(2)

len(dir(a))

len(dir(b)) : 둘은 같다.



len(dir(Y))
len(dir(X))  :  클래스로 비교해도 같다


@property
def get_x(self, y):

property는 기본적으로 파라미터를 사용하지 않는다.

property는 메소드지만 변수처럼 사용한다.

... .sq처럼 그냥 변수처럼 사용한다.
괄호가 없다.

@property




그 동안 클래스에 @ 붙여서 쓰는 거 배운것 : @classmethod, @property, 이제 @staticmethod

@staticmethod : 이걸 쓰면 self 사용 안해도 된다.


dir은 인스턴스 변수 안나온다. 인스턴스와 클래스가 다르게 나온다.

vars(클래스이름) : 할 수 있는것 모두 씀.




@staticmethod
def t():
    return 3
    
여기에는 self가 안들어가도된다.

만약 t(s)처럼 s가 들어갔다면 s는 그냥 변수이다.


데코레이터는 함수의 기능을 바꾸는 애.



@어노테이션 붙여둔 것들은 class이름 vars했을 때, 그 이름으로 출력된다.

mappingproxy({'__dict__': <attribute '__dict__' of 'Y' objects>,
              '__doc__': None,
              '__init__': <function __main__.Y.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Y' objects>,
              'b': <function __main__.Y.b>,
              's': <classmethod at 0x1ee5c3719b0>,
              'sq': <property at 0x1ee5c3e2778>,
              't': <staticmethod at 0x1ee5c371be0>})
              


              
메타클래스. type(type) : type



t = type('xxx', (int,), {'a':1})

객체도 동적으로 만들 수 있다.


데코레이터, 오버라이딩 : 함수의 기능을 바꿀 수 있다.



iter : function, 혼돈하기 쉽다.




class MyType(type):
    pass

class MySpecialClass(metaclass=MyType):
    pass
    
    
metaclass를 하기 위해서는 3가지 인자가 필요하다.


t = MyType()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-be215be46670> in <module>()
----> 1 t = MyType()

TypeError: type.__new__() takes exactly 3 arguments (0 given)


Singleton a를 만들고 b를 만들어도 메모리값이 바뀌지 않는다.

metaclass는 class의 새로운 기능을 만드는 애다.



class Singleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if not cls.instance:
             cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance
        
이 자체는 타입을 상속받아서 메타클래스이다.


class ASingleton(metaclass=Singleton):
    pass
    
metaclass라는 키워드 기반으로 ASingleton이라는 메타클래스를 만들었다.

class ASingleton(metaclass=Singleton):
    pass

    
a = ASingleton()
b = ASingleton()
a is b : True

hex(id(a))
'0x15e8bdad4a8'

hex(id(b))
'0x15e8bdad4a8'


싱글톤은 값을 만들면 공유하고 id가 같다. 메타클래스로 싱글톤을 만드는 것이 가장 어려운 방식이다.

클래스 자체의 기능을 바꿔버리는 것이 메타클래스.


클래스를 메타클래스 하나로만 만들고, 그 아래에 클래스를 여러 번 만들어서 변경시켜서 하나의 id를 가지게 한다.



__init__랑 __new__의 차이 : __init__은 인스턴스가 먼저옴, __new__는 클래스를 먼저 만들고 클래스에서 인스턴스 여럿을 만들 수 있다.

new는 항상 인스턴스를 반환한다.

return obj(이건 인스턴스임)



클래스가 new불러서 인스턴스가 init를 호출한다.

인스턴스 만들 때 hooking 가능, 클래스 만들 때 hooking 가능






abc 중요함.

abstract base class : 행동하는 애는 아니고 만들어 놓으면 구체적 구현은 니가 해라.
정의만 해둔 것. C++의 virtual abstract와 같은 개념.



from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

MyABC.register(tuple)

ABCMeta 불러와서 tuple 등록


assert, True면 에러안나고 False면 에러난다.