ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python]메타클래스 & 인스턴스, 클래스, 스태틱 메소드 개념 정리
    💻 프로그래밍/Python 2018. 4. 30. 18:53

    안녕하세요! 코딩하는 JAY입니다. 

    오늘은 파이썬 메타클래스, 인스턴스 메소드, 클래스 메소드, 스태틱 메소드에 대해 정리해보도록 하겠습니다.

    처음 Python에서 클래스 개념을 공부할때, C++이랑 개념이 조금 달라 많이 헷갈렸던 것 같습니다.


    * 메타클래스(metaclass) : 클래스를 만드는 무언가(?)

    뭔가 설명이 이상해 보이네요 ㅎㅎ C++과는 다르게 파이썬에서 클래스는 그 자체로 객체입니다. 클래스가 정의되면서 메모리에 공간을 차지하게 됩니다. 그리고 메타클래스는 이런 클래스를 만들어 주는 역할을 합니다. 

    실제로 예제를 통해 메타클래스에 대해서 좀 더 알아보도록 해보겠습니다. 파이썬에서는 데이터 타입을 확인하기 위해 type() 키워드를 사용합니다. 

    1
    2
    3
    print(type(100)) # int 
    print(type('test')) # string
    print(type(10.9)) # float




    위 예제의 결과 처럼 100 은 int형 타입, 'test'는 string형 타입, 10.9는 float형 타입이라고 출력 되었습니다.

    위 설명에서 파이썬 클래스는 그 자체로 객체이며, 메타클래스는 이런 클래스를 만들어 주는 역할이라고 설명했습니다. 그럼 이제 이 int, str, float형의 타입을 알아 보도록 하겠습니다. (추가로 클래스를 만들어서 그 클래스의 타입도 확인해보겠습니다.)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class human(object) :
        def __init__(self, name, age) :
            self.name = name
            self.age = age
     
        def introduce(self) :
            print('이름 {}, 나이 {}'.format(self.name, self.age))
     
    print(type(human))
    print(type(int))
    print(type(str))
    print(type(float))


    카카오 물음표에 대한 이미지 검색결과

    전부 데이터 타입이 'type'이라고 나왔습니다. 맞습니다!! 클래스의 메타클래스는 'type'입니다. type은 메타클래스 그 자체입니다. 그래서 int, str, float 그리고 사용자가 만든 클래스의 데이터타입이 'type'으로 출력된 것 입니다. 이제 좀 메타클래스에 대해서 이해가 되시나요?

    그럼 메타클래스는 왜 사용할까요? 메타클래스는 API를 개발할때 쓰인다고 합니다. 그리고 우리가 사용하는 파이썬 프로그래밍에서 99%는 메타클래스를 사용하지 않고 구현 가능하다고 합니다. 이 부분에 대한 자세한 내용은 아래 URL을 참고하시길 바랍니다!

    메타클래스의 이해 : https://tech.ssut.me/2017/03/24/understanding-python-metaclasses/


    1. 인스턴스 메소드(instance method) 

    - 인스턴스에 따라서 다르게 동작해야하는 메소드입니다.

    - 인스턴스 혹은 변수에 따라서 다르게 동작되어야 하는 메소드들은 인스턴스 소속으로 사용합니다.

    - 인스턴스를 통해서만 호출이 됩니다.

    - 첫번째 인자로 'self'를 관습적으로 사용하며 인스턴스 자신을 전달합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class test(object) :
        def instanceFunction(self, x) :
            self.sNum = x
            print(self.sNum)
     
        def instanceFunctionTest(self) :
            print(self.sNum)
     
    #비교하기
    = test()
    = test()
    = test()
     
    print("인스턴스메소드 비교")
    A.instanceFunction(100)
    B.instanceFunction(200)
    C.instanceFunction(300)
    print()
    A.instanceFunctionTest()
    B.instanceFunctionTest()
    C.instanceFunctionTest()


    위 코드를 동작시켜보면 인스턴스 마다 변수가 다르게 출력되는 것을 확인할 수 있습니다.

    인스턴스 메소드는 인스턴스를 통해서만 호출된다고 했습니다. 그렇지 않으면 아래와 같은 에러가 나옵니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class test(object) :
        testNum = 0
        def instanceFunction(self, x) :
            self.sNum = x
            print(self.sNum)
     
        def instanceFunctionTest(self) :
            print(self.sNum)
     
        @classmethod
        def ClassMethodTest(cls) :
            print(cls.testNum)
     
     
    #비교하기
    = test()
    = test()
    = test()
     
    test.ClassMethodTest()
    test.instanceFunctionTest() # 클래스에서 인스턴스메소드로 접근하게 되면 error
    A.instanceFunctionTest()
    A.ClassMethodTest()
     
    cs




    2. 클래스 메소드(class method) 

    - 클래스에서 직접 접근하는 메소드 입니다.

    - 인스턴스에 내부적인 데이터(변수)하고 상관없이 동작하는 메서드는 클래스 소속으로 사용하게 됩니다.

    - "@classmethod"라는 데코레이터로 정의합니다.

    첫번째 인자로 'cls'를 관습적으로 사용하며 클래스 자신을 전달합니다.

    - 클래스 메소드의 경우 인스턴스, 클래스 두 가지 접근이 모두 가능합니다.


    클래스메소드 설명과 함께 변수의 스코프에 대해서 같이 알아보도록 하겠습니다.(약간 조금 이야기가 세는 기분인데;; 이 부분도 처음에 클래스 메소드를 공부하면서 헷갈리더라고요ㅎㅎㅎ)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class test(object) :
        testNum = 0
        def instanceFunction(self, x) :
            self.sNum = x
            print(self.sNum)
     
        def instanceFunctionTest(self) :
            print(self.sNum)
     
        @classmethod
        def ClassMethodTest(cls) :
            print(testNum)
     
    #비교하기
    = test()
    = test()
    = test()
     
    print("클래스메소드 비교")
    print('클래스에서 직접 접근한 값 : {}'.format(test.testNum))
    print('A의 testNum 값 : {}'.format(A.testNum))
    print('B의 testNum 값 : {}'.format(B.testNum))
    print('C의 testNum 값 : {}'.format(C.testNum))
    print('\n클래스로 접근해서 testNum값을 99로 변경')
    test.testNum = 99 # 값을 변경
    print('A의 testNum 값 : {}'.format(A.testNum))
    print('B의 testNum 값 : {}'.format(B.testNum))
    print('C의 testNum 값 : {}'.format(C.testNum))




    결과를 보시면 test클래스로 직접 testNum에 접근해서 값을 99로 바뀌고 A,B,C 인스턴스의 testNum을 출력할 결과 같은 값이 출력되는 것을 확인하실수가 있습니다. 즉, 모든 인스턴스가 값을 공유하는 것 입니다. 그럼 다음과 같은 경우는 어떻게 실행될까요?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class test(object) :
        testNum = 0
        def instanceFunction(self, x) :
            self.sNum = x
            print(self.sNum)
     
        def instanceFunctionTest(self) :
            print(self.sNum)
     
        @classmethod
        def ClassMethodTest(cls) :
            print(cls.testNum)
     
    #비교하기
    = test()
    = test()
    = test()
     
    print("클래스메소드 비교")
    print('클래스에서 직접 접근한 값 : {}'.format(test.testNum))
    print('A의 testNum 값 : {}'.format(A.testNum))
    print('B의 testNum 값 : {}'.format(B.testNum))
    print('C의 testNum 값 : {}'.format(C.testNum))
    print('\n클래스로 접근해서 testNum값을 99로 변경')
    test.testNum = 99 # 값을 변경
    A.testNum = 10
    print('A의 testNum 값 : {}'.format(A.testNum))
    A.ClassMethodTest()
    print('B의 testNum 값 : {}'.format(B.testNum))
    print('C의 testNum 값 : {}'.format(C.testNum))



    A(인스턴스)로 testNum을 접근하여 값을 10으로 바꾼결과, A의 testNum 값만 10을 출력 되었습니다. 왜 그런걸까요? 정말 간단합니다. 우리가 A(인스턴스)로 접근해서 testNum값을 정의해 주었기 때문입니다. 

    파이썬에서는 안에 바같으로(지역-> 전역) 변수를 찾습니다. 처음 A.testNum을 정의해주지 않았을때는 인스턴스안에 testNum이 없기때문에 A의 부모클래스인 test의 testNum(클래스변수)을 출력했던 것 입니다. 하지만 A.testNum = 10 을 정의함으로써 인스턴스에 안의 testNum을  출력하는 것 입니다. A.ClassMethodTest()로  cls.testNum을 출력하면 99가 나오는 것을 확인 할 수 있습니다.


    3. 스태틱 메소드(static mthod)

    - 클래스 안에서만 정의가 가능한 메소드 입니다.

    - 스태틱 메소드는 인스턴스 메소드, 클래스 메소드와는 달리 인자를 받지 않습니다.

    - 스태틱 메소드는 클래스 안에서만 사용할 수 있다는 점만 빼면 일반함수와 동일 합니다. 

    - 스태틱 메소드의 경우 클래스 메소드와 마찬가지로 인스턴스, 클래스 두 가지 접근이 모두 가능합니다.(이 부분이 조금 헷갈리는 부분이네요;;)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class test(object) :
        testNum = 0
        def instanceFunction(self, x) :
            self.sNum = x
            print(self.sNum)
     
        def instanceFunctionTest(self) :
            print(self.sNum)
     
        @classmethod
        def ClassMethodTest(cls) :
            print(cls.testNum)
     
        @staticmethod
        def staticmethodTest(a, b) :
            print(a + b)
     
    #비교하기
    = test()
    = test()
    = test()
     
    test.staticmethodTest(21)
    A.staticmethodTest(34)




     그리고 정적함수이기 때문에 self로 접근은 불가하며, 아래처럼 에러가 납니다.


    오늘의 포스팅은 여기까지 입니다. 나름 잘 정리해보려고 했는데, 내용이 잘 전달 되었을지 모르겠네요ㅠㅠ 혹시 궁금한 점이나 잘못된 부분이 있다면 댓글로 남겨주시길 바랍니다. 지금까지 코딩하는 JAY 였습니다:D

    카카오 야근에 대한 이미지 검색결과

    오늘도 좋은하루 되시고, 좋은 코드 생산하세요!!:D

    댓글

운동하는 개발자 JAY-JI