{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "***\n", "# 19. 클래스와 객체\n", "***\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 1 파이썬 클래스와 이름 공간\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 파이썬 클래스(Class)는 새로운 이름 공간을 지원하는 또 다른 단위\n", "- 객체 지향 프로그래밍에서 클래스(Class)의 역할\n", " - 사용자 정의 타입(Type) 생성\n", "- 클래스 정의 구문\n", "> class 클래스 이름: #헤더(Header)
\n", ">         pass #몸체(Body)\n", "- 인스턴스: 클래스로 부터 만들어낸 객체\n", "- 모듈 vs. 클래스 vs. 인스턴스\n", " - 모듈: 파일 단위로 이름 공간을 구성\n", " - 클래스: 클래스 영역 내에 이름 공간을 구성\n", " - 인스턴스: 인스턴스 영역 내에 이름 공간을 구성" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "\n", "2\n", "\n", "['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b']\n", "['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']\n" ] } ], "source": [ "class S1:\n", " a = 1\n", " \n", "print(S1.a)\n", "print()\n", "\n", "S1.b = 2 # 클래스 이름 공간에 새로운 이름의 생성\n", "print(S1.b)\n", "print()\n", "\n", "print(dir(S1)) # S1에 포함된 이름들을 리스트로 반환\n", "del S1.b # 이름 공간 S1에서 b삭제\n", "print(dir(S1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 파이썬에서는 동적으로 인스턴스 외부에서 인스턴스 멤버를 추가할 수 있음\n", " - 클래스와 독립적으로 각 인스턴스를 하나의 이름 공간으로 취급함" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "10\n", "1\n" ] } ], "source": [ "x = S1() # x는 S1의 클래스 인스턴스\n", "print(x.a)\n", "\n", "x.a = 10 # 클래스 인스턴스 x의 이름 공간에 이름 생성\n", "print(x.a)\n", "\n", "print(S1.a) # 클래스 이름 공간과 클래스 인스턴스의 이름공간은 다르다" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "300\n", "10\n", "1\n" ] } ], "source": [ "y = S1() # S1 클래스의 또 다른 인스턴스 생성\n", "\n", "y.a = 300 # 클래스 인스턴스 y의 이름 공간에 이름 생성\n", "\n", "print(y.a)\n", "print(x.a) # x 인스턴스 공간의 이름 a 확인\n", "print(S1.a) # 클래스 이름 공간의 a 확인" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "class Simple:\n", " pass\n", "\n", "s1 = Simple()\n", "s2 = Simple()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n", "3\n", "2\n", "\n", "[1]\n" ] }, { "ename": "AttributeError", "evalue": "'Simple' object has no attribute 'stack'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 최종 s1.stack값\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# s2에는 stack을 정의한 적이 없다.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;31m#기존에러:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAttributeError\u001b[0m: 'Simple' object has no attribute 'stack'" ] } ], "source": [ "s1.stack = [] # 동적으로 클래스 인스턴스 이름 공간 안에 새로운 변수(이름) stack 생성\n", "s1.stack.append(1) # 값 추가\n", "s1.stack.append(2)\n", "s1.stack.append(3)\n", "\n", "print(s1.stack)\n", "print(s1.stack.pop()) \n", "print(s1.stack.pop())\n", "print()\n", "print(s1.stack) # 최종 s1.stack값\n", "print(s2.stack) # s2에는 stack을 정의한 적이 없다.\n", "\n", "#기존에러:\n", "#AttributeError: Simple instance has no attribute 'stack'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](../images/instance.png)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "del s1.stack # s1에서 stack삭제" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 2 메쏘드의 정의와 호출\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-1 멤버 메쏘드의 정의와 호출" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스 내부에 객체 멤버 메소드 선언 - def 키워드 사용\n", " - 일반 함수와 다른 점은 첫번째 인수로 self 사용 (self라는 이름은 관례적)\n", " - self: 인스턴스 객체 자신의 레퍼런스를 지니고 있음\n", " - 각 인스턴스들은 self를 이용하여 자신의 이름 공간에 접근 " ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "class MyClass:\n", " def set(self, v):\n", " self.value = v\n", " \n", " def get(self):\n", " return self.value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 인스턴스 객체를 통하여 멤버 메소드를 호출할 때 self 인자는 없다고 생각" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "egg\n", "egg\n" ] } ], "source": [ "c = MyClass() # 인스턴스 생성 \n", "c.set('egg') # 메소드 set 호출\n", "print(c.get()) # 메소드 get 호출\n", "print(c.value) # 인스턴스 변수에 직접 접근" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 위 코드는 실제로 아래 코드와 동일함" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "egg\n", "egg\n" ] } ], "source": [ "c = MyClass() # 인스턴스 생성 \n", "MyClass.set(c, 'egg')\n", "print(MyClass.get(c))\n", "print(c.value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 멤버 메소드 호출 종류\n", " - Unbound method call: 클래스 객체를 이용한 메소드 호출\n", " - 예: MyClass.set(c, 'egg')\n", " - Bound method call: 인스턴스 객체를 통한 메소드 호출 (self 인자는 호출받은 객체가 자동으로 할당)\n", " - 예: c.set('egg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-2 객체 내부에서의 다른 맴버 메쏘드 호출" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "\n", "2\n" ] } ], "source": [ "class MyClass:\n", " def set(self, v):\n", " self.value = v\n", " \n", " def incr(self):\n", " self.set(self.value + 1) # 내부 메소드 호출\n", "\n", " def get(self):\n", " return self.value\n", " \n", "c = MyClass()\n", "c.set(1)\n", "print(c.get())\n", "\n", "print()\n", "\n", "c.incr()\n", "print(c.get())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- [NOTE!!!] 만약 위 코드에서 self.set(self.value + 1)set(self.value + 1)으로 바꾸면 set 함수를 클래스 외부에서 찾는다. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "\n", "set function outside function - 2\n", "1\n" ] } ], "source": [ "def set(i):\n", " print(\"set function outside function - \", i)\n", " \n", "class MyClass:\n", " def set(self, v):\n", " self.value = v\n", " \n", " def incr(self):\n", " set(self.value + 1) # 클래스 외부에 존재하는 set 메소드 호출\n", "\n", " def get(self):\n", " return self.value\n", " \n", "c = MyClass()\n", "c.set(1)\n", "print(c.get())\n", "\n", "print()\n", "\n", "c.incr()\n", "print(c.get())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-3 정적 메소드(static method)\n", "- 정적 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 직접 호출할 수 있는 메소드\n", " - self를 인자로 할당하지 않음\n", " - [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능\n", " \n", "- 장식자(Decorator) @staticmethod 활용 " ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "static method 1 2\n", "\n", "static method 1 2\n" ] } ], "source": [ "class D:\n", " @staticmethod\n", " def spam(x, y): # self가 없다.\n", " print('static method', x, y)\n", " \n", "D.spam(1, 2) # 인스턴스 객체 없이 클래스에서 직접 호출\n", "\n", "print()\n", "d = D()\n", "d.spam(1, 2) # 인스턴스 객체를 통해서도 호출 가능" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-4 클래스 메소드(class method)\n", "- 클래스 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 호출하며 ***첫 인수로 클래스 객체를 자동으로 받는 메소드***\n", " - [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능\n", "- 장식자(Decorator) @classmethod 활용 " ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", " -> 5\n", " -> 5\n" ] } ], "source": [ "class C:\n", " @classmethod\n", " def spam(cls, y):\n", " print(cls, '->', y)\n", " \n", "print(C)\n", "\n", "C.spam(5) # 첫번째 인수로 C가 잠재적으로 전달된다.\n", "\n", "c = C()\n", "c.spam(5) # 인스턴스 객체를 통해서도 호출 가능." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 상속받은 서브 클래스를 통해 호출하면, 첫 인수에는 서브 클래스 객체가 자동으로 할당됨 " ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " -> 3\n", "None\n", "\n", " -> 3\n", "None\n", "\n", " -> 3\n", "None\n" ] } ], "source": [ "class D(C):\n", " pass\n", "\n", "print(D.spam(3))\n", "print()\n", "\n", "d = D()\n", "print(d.spam(3))\n", "print()\n", "\n", "print(C.spam(3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 3 클래스 멤버와 인스턴스 멤버\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스 멤버 vs. 인스턴스 멤버\n", " - 클래스 멤버\n", " - 클래스 이름 공간에 생성됨\n", " - 모든 인스턴스들에 의해 공유됨\n", " - 인스턴스 멤버\n", " - 인스턴스 이름 공간에 생성됨\n", " - 각각의 인스턴스 마다 독립성이 보장됨" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "class Var:\n", " c_mem = 100 # 클래스 멤버 정의\n", " \n", " def f(self): \n", " self.i_mem = 200 # 인스턴스 멤버 정의\n", "\n", " def g(self):\n", " print(self.i_mem)\n", " print(self.c_mem)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n", "100\n", "200\n", "-------\n", "100\n" ] }, { "ename": "AttributeError", "evalue": "'Var' object has no attribute 'i_mem'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mv2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mVar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 인스턴스 v2 생성\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mc_mem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mi_mem\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'Var' object has no attribute 'i_mem'" ] } ], "source": [ "print(Var.c_mem) # 클래스 객체를 통하여 클래스 멤버 접근\n", "\n", "v1 = Var() # 인스턴스 v1 생성\n", "print(v1.c_mem) # 인스턴스를 통하여 클래스 멤버 접근\n", "v1.f() # 인스턴스 멤버 i_mem이 생성됨\n", "print(v1.i_mem) # 인스턴스 v1을 통하여 인스턴스 멤버 접근\n", "\n", "print(\"-------\")\n", "v2 = Var() # 인스턴스 v2 생성\n", "print(v2.c_mem)\n", "print(v2.i_mem) # 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](../images/instance2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \"인스턴스 이름.멤버 이름\"으로 멤버를 참조할 때 멤버의 검색 순서\n", " - 1) 인스턴스 멤버\n", " - 2) 인스턴스 멤버가 없다면 클래스 멤버" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n", "100\n", "\n", "50\n", "100\n", "100\n" ] } ], "source": [ "print(v1.c_mem) # 인스턴스 v1을 통해 클래스 멤버 참조\n", "print(v2.c_mem) # 인스턴스 v2를 통해 클래스 멤버 참조\n", "\n", "print()\n", "v1.c_mem = 50 # 인스턴스 이름 공간에 c_mem생성\n", "print(v1.c_mem) # 인스턴스 v1을 통해 인스턴스 멤버 참조\n", "print(v2.c_mem) # 인스턴스 v2을 통해 클래스 멤버참조 (인스턴스 멤버가 없으므로, 클래스 멤버 참조)\n", "print(Var.c_mem) # 클래스 멤버참조" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](../images/instance3.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 4 생성자와 소멸자\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_init__: 생성자 메소드\n", " - 객체가 생성될 때 자동으로 불리어지는 메소드\n", " - 일반적으로 객체가 보유해야 할 변수나 자원들의 초기화하는 코드를 작성함\n", " - self 인자가 정의되어야 함\n", " \n", "- \\_\\_del__: 소멸자 메소드\n", " - 객체가 소멸 (메모리에서 해제)될 때 자동으로 불리어지는 메소드\n", " - 일반적으로 객체가 점유하고 있는 메모리나 기타 자원들의 해제하는 코드를 작성함\n", " - self 인자가 정의되어야 함\n", " - 개발자가 특별히 작성하지 않아도 될 메소드\n", " - 이유: 파이썬에서는 객체가 점유하고 있는 메모리나 기타 자원들의 해제가 자동으로 되기 때문에\n", "- [참고] \\_\\_ (연속된 두 개의 언더라인)의 의미: 예약된 (특수) 이름" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 다음 코드에 대한 설명\n", " - mylife = Life() 로서 로컬 변수에 할당되는 인스턴스 mylife가 생성되는 순간 \\_\\_init__ 생성자 메소드 호출\n", " - sleep(3)에 의해 3초간 sleep 상태\n", " - 3초 이후 함수가 리턴됨 --> 로컬 변수, 즉 mylife 객체가 메모리에서 해제됨 --> \\_\\_del__ 소멸자 메소드 호출" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Birthday Wed Nov 28 22:30:18 2018\n", "Sleeping for 3 sec\n", "Deathday Wed Nov 28 22:30:21 2018\n" ] } ], "source": [ "# _*_ coding:utf-8 _*_\n", "from time import ctime, sleep\n", "\n", "class Life:\n", " def __init__(self): # 생성자\n", " self.birth = ctime() # 현재시간에 대한 문자열을 얻는다.\n", " print('Birthday', self.birth) # 현재 시간 출력\n", " \n", " def __del__(self): # 소멸자\n", " print('Deathday', ctime()) # 소멸 시간 출력 \n", "\n", "def test():\n", " mylife = Life()\n", " print('Sleeping for 3 sec')\n", " sleep(3) #3초간 sleep(block)상태에 있음 (즉, 3초간 CPU 점유 못함)\n", "\n", "test()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 인자를 받는 생성자 호출 가능\n", "- [참고] \\_\\_str\\_\\_: print 예약어나 str() 내장함수 호출에 대응되는 메소드" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "self.i:10\n", "self.i:10\n" ] } ], "source": [ "class Integer:\n", " def __init__(self, i):\n", " self.i = i\n", " def __str__(self):\n", " return \"self.i:\" + str(self.i)\n", "\n", "i = Integer(10)\n", "print(i)\n", "print(str(i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

참고 문헌: 파이썬(열혈강의)(개정판 VER.2), 이강성, FreeLec, 2005년 8월 29일

" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 1 }