💻 프로그래밍/Python

[Python]메타클래스 & 인스턴스, 클래스, 스태틱 메소드 개념 정리

피트웨어 제이 (FitwareJay) 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