[Python]메타클래스 & 인스턴스, 클래스, 스태틱 메소드 개념 정리
안녕하세요! 코딩하는 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) #비교하기 A = test() B = test() C = 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) #비교하기 A = test() B = test() C = 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) #비교하기 A = test() B = test() C = 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) #비교하기 A = test() B = test() C = 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) #비교하기 A = test() B = test() C = test() test.staticmethodTest(2, 1) A.staticmethodTest(3, 4) |
그리고 정적함수이기 때문에 self로 접근은 불가하며, 아래처럼 에러가 납니다.
오늘의 포스팅은 여기까지 입니다. 나름 잘 정리해보려고 했는데, 내용이 잘 전달 되었을지 모르겠네요ㅠㅠ 혹시 궁금한 점이나 잘못된 부분이 있다면 댓글로 남겨주시길 바랍니다. 지금까지 코딩하는 JAY 였습니다:D
오늘도 좋은하루 되시고, 좋은 코드 생산하세요!!:D