-
[Python]메타클래스 & 인스턴스, 클래스, 스태틱 메소드 개념 정리💻 프로그래밍/Python 2018. 4. 30. 18:53
안녕하세요! 코딩하는 JAY입니다.
오늘은 파이썬 메타클래스, 인스턴스 메소드, 클래스 메소드, 스태틱 메소드에 대해 정리해보도록 하겠습니다.
처음 Python에서 클래스 개념을 공부할때, C++이랑 개념이 조금 달라 많이 헷갈렸던 것 같습니다.
* 메타클래스(metaclass) : 클래스를 만드는 무언가(?)
뭔가 설명이 이상해 보이네요 ㅎㅎ C++과는 다르게 파이썬에서 클래스는 그 자체로 객체입니다. 클래스가 정의되면서 메모리에 공간을 차지하게 됩니다. 그리고 메타클래스는 이런 클래스를 만들어 주는 역할을 합니다.
실제로 예제를 통해 메타클래스에 대해서 좀 더 알아보도록 해보겠습니다. 파이썬에서는 데이터 타입을 확인하기 위해 type() 키워드를 사용합니다.
123print(type(100)) # intprint(type('test')) # stringprint(type(10.9)) # float위 예제의 결과 처럼 100 은 int형 타입, 'test'는 string형 타입, 10.9는 float형 타입이라고 출력 되었습니다.
위 설명에서 파이썬 클래스는 그 자체로 객체이며, 메타클래스는 이런 클래스를 만들어 주는 역할이라고 설명했습니다. 그럼 이제 이 int, str, float형의 타입을 알아 보도록 하겠습니다. (추가로 클래스를 만들어서 그 클래스의 타입도 확인해보겠습니다.)
123456789101112class human(object) :def __init__(self, name, age) :self.name = nameself.age = agedef 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'를 관습적으로 사용하며 인스턴스 자신을 전달합니다.
123456789101112131415161718192021class test(object) :def instanceFunction(self, x) :self.sNum = xprint(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()위 코드를 동작시켜보면 인스턴스 마다 변수가 다르게 출력되는 것을 확인할 수 있습니다.
인스턴스 메소드는 인스턴스를 통해서만 호출된다고 했습니다. 그렇지 않으면 아래와 같은 에러가 나옵니다.
123456789101112131415161718192021222324class test(object) :testNum = 0def instanceFunction(self, x) :self.sNum = xprint(self.sNum)def instanceFunctionTest(self) :print(self.sNum)@classmethoddef ClassMethodTest(cls) :print(cls.testNum)#비교하기A = test()B = test()C = test()test.ClassMethodTest()test.instanceFunctionTest() # 클래스에서 인스턴스메소드로 접근하게 되면 errorA.instanceFunctionTest()A.ClassMethodTest()cs 2. 클래스 메소드(class method)
- 클래스에서 직접 접근하는 메소드 입니다.
- 인스턴스에 내부적인 데이터(변수)하고 상관없이 동작하는 메서드는 클래스 소속으로 사용하게 됩니다.
- "@classmethod"라는 데코레이터로 정의합니다.
- 첫번째 인자로 'cls'를 관습적으로 사용하며 클래스 자신을 전달합니다.
- 클래스 메소드의 경우 인스턴스, 클래스 두 가지 접근이 모두 가능합니다.
클래스메소드 설명과 함께 변수의 스코프에 대해서 같이 알아보도록 하겠습니다.(약간 조금 이야기가 세는 기분인데;; 이 부분도 처음에 클래스 메소드를 공부하면서 헷갈리더라고요ㅎㅎㅎ)
12345678910111213141516171819202122232425262728class test(object) :testNum = 0def instanceFunction(self, x) :self.sNum = xprint(self.sNum)def instanceFunctionTest(self) :print(self.sNum)@classmethoddef 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을 출력할 결과 같은 값이 출력되는 것을 확인하실수가 있습니다. 즉, 모든 인스턴스가 값을 공유하는 것 입니다. 그럼 다음과 같은 경우는 어떻게 실행될까요?
123456789101112131415161718192021222324252627282930class test(object) :testNum = 0def instanceFunction(self, x) :self.sNum = xprint(self.sNum)def instanceFunctionTest(self) :print(self.sNum)@classmethoddef 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 = 10print('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)
- 클래스 안에서만 정의가 가능한 메소드 입니다.
- 스태틱 메소드는 인스턴스 메소드, 클래스 메소드와는 달리 인자를 받지 않습니다.
- 스태틱 메소드는 클래스 안에서만 사용할 수 있다는 점만 빼면 일반함수와 동일 합니다.
- 스태틱 메소드의 경우 클래스 메소드와 마찬가지로 인스턴스, 클래스 두 가지 접근이 모두 가능합니다.(이 부분이 조금 헷갈리는 부분이네요;;)
123456789101112131415161718192021222324class test(object) :testNum = 0def instanceFunction(self, x) :self.sNum = xprint(self.sNum)def instanceFunctionTest(self) :print(self.sNum)@classmethoddef ClassMethodTest(cls) :print(cls.testNum)@staticmethoddef staticmethodTest(a, b) :print(a + b)#비교하기A = test()B = test()C = test()test.staticmethodTest(2, 1)A.staticmethodTest(3, 4)그리고 정적함수이기 때문에 self로 접근은 불가하며, 아래처럼 에러가 납니다.
오늘의 포스팅은 여기까지 입니다. 나름 잘 정리해보려고 했는데, 내용이 잘 전달 되었을지 모르겠네요ㅠㅠ 혹시 궁금한 점이나 잘못된 부분이 있다면 댓글로 남겨주시길 바랍니다. 지금까지 코딩하는 JAY 였습니다:D
오늘도 좋은하루 되시고, 좋은 코드 생산하세요!!:D
'💻 프로그래밍 > Python' 카테고리의 다른 글
[RxPy] Reactive Programming? Rx? 그게뭐야? (2) 2020.06.23 [BitBar] 오픈소스 플러그인 내맘대로 만들어보기 (Mac OS 메뉴바 플러그인) (0) 2020.06.14 우분투에서 python 가상환경(venv) 설치 안될 경우 (1) 2018.01.09 BeautifulSoup로 웹 크롤링 하기 (1) 2017.10.29 Python과 SQLite3를 사용해 DB만들기 (0) 2017.10.25