{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "***\n", "# 21. 상속과 다형성\n", "***\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 1 클래스 상속\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-1 클래스 상속과 이름 공간의 관계" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 상속의 이유\n", " - 코드의 재사용\n", " - 상속받은 자식 클래스는 상속을 해준 부모 클래스의 모든 기능을 그대로 사용\n", " - 자식 클래스는 필요한 기능만을 정의하거나 기존의 기능을 변경할 수 있음" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Person:\n", " def __init__(self, name, phone=None):\n", " self.name = name\n", " self.phone = phone\n", " def __str__(self):\n", " return '' % (self.name, self.phone)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Employee(Person): # 괄호 안에 쓰여진 클래스는 슈퍼클래스를 의미한다.\n", " def __init__(self, name, phone, position, salary):\n", " Person.__init__(self, name, phone) # Person클래스의 생성자 호출\n", " self.position = position\n", " self.salary = salary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 이름 공간의 포함관계\n", " - 자식 클래스 > 부모 클래스" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](images/inheritance2.png)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "홍길동\n", "\n", "\n", "손창희 대리\n", "\n", "김기동 과장\n", "\n" ] } ], "source": [ "p1 = Person('홍길동', 1498)\n", "print p1.name\n", "print p1\n", "\n", "print\n", "\n", "m1 = Employee('손창희', 5564, '대리', 200)\n", "m2 = Employee('김기동', 8546, '과장', 300)\n", "print m1.name, m1.position # 슈퍼클래스와 서브클래스의 멤버를 하나씩 출력한다.\n", "print m1\n", "print m2.name, m2.position\n", "print m2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](images/inheritance3.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-2 생성자 호출\n", "- 서브 클래스의 생성자는 슈퍼 클래스의 생성자를 자동으로 호출하지 않는다." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sub init called\n" ] } ], "source": [ "class Super:\n", " def __init__(self):\n", " print 'Super init called'\n", "\n", "class Sub(Super):\n", " def __init__(self):\n", " print 'Sub init called'\n", " \n", "s = Sub()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 서브 클래스의 생성자에서 슈퍼 클래스의 생성자를 명시적으로 호출해야 한다." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Super init called\n", "Sub init called\n" ] } ], "source": [ "class Super:\n", " def __init__(self):\n", " print 'Super init called'\n", " \n", "class Sub(Super):\n", " def __init__(self):\n", " Super.__init__(self) # 명시적으로 슈퍼클래스의 생성자를 호출한다.\n", " print 'Sub init called'\n", " \n", "s = Sub()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 서브 클래스에 생성자가 정의되어 있지 않은 경우에는 슈퍼 클래스의 생성자가 호출된다. " ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Super init called\n" ] } ], "source": [ "class Super:\n", " def __init__(self):\n", " print 'Super init called'\n", " \n", "class Sub(Super):\n", " pass\n", "\n", "s = Sub()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-3 메쏘드의 대치 (메소드 오버라이드 - Override)\n", "- 서브 클래스에서 슈퍼 클래스에 정의된 메소드를 재정의하여 대치하는 기능" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n" ] } ], "source": [ "class Person:\n", " def __init__(self, name, phone=None):\n", " self.name = name\n", " self.phone = phone\n", " def __str__(self):\n", " return '' % (self.name, self.phone)\n", " \n", "class Employee(Person):\n", " def __init__(self, name, phone, position, salary):\n", " Person.__init__(self, name, phone)\n", " self.position = position\n", " self.salary = salary\n", " \n", "p1 = Person('gslee', 5284)\n", "m1 = Employee('kslee', 5224, 'President', 500)\n", "\n", "print p1\n", "print m1" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n" ] } ], "source": [ "class Employee(Person):\n", " def __init__(self, name, phone, position, salary):\n", " Person.__init__(self, name, phone)\n", " self.position = position\n", " self.salary = salary\n", " def __str__(self):\n", " return '' % (self.name, self.phone, self.position, self.salary)\n", " \n", "p1 = Person('gslee', 5284)\n", "m1 = Employee('kslee', 5224, 'President', 500)\n", "\n", "print p1\n", "print m1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-4 다형성(Polymorphism)\n", "- 상속 관계 내의 다른 클래스들의 인스턴스들이 같은 멤버 함수 호출에 대해 각각 다르게 반응하도록 하는 기능\n", " - 연산자 오버로딩도 다형성을 지원하는 중요한 기술 \n", " - 예를 들어, a와 b의 객체 형에 따라 a + b의 + 연산자 행동 방식이 변경되는 것\n", "\n", "- 다형성의 장점\n", " - 적은 코딩으로 다양한 객체들에게 유사한 작업을 수행시킬 수 있음\n", " - 프로그램 작성 코드 량이 줄어든다.\n", " - 코드의 가독성을 높혀준다.\n", " \n", "- 파이썬에서 다형성의 장점\n", " - 형 선언이 없다는 점에서 파이썬에서는 다형성을 적용하기가 더욱 용이하다.\n", " - 실시간으로 객체의 형이 결정되므로 단 하나의 메소드에 의해 처리될 수 있는 객체의 종류에 제한이 없다.\n", " - 즉, 다른 언어보다 코드의 양이 더욱 줄어든다." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "멍멍\n", "꽥꽥\n", "...\n" ] } ], "source": [ "class Animal:\n", " def cry(self):\n", " print '...'\n", " \n", "class Dog(Animal):\n", " def cry(self):\n", " print '멍멍'\n", " \n", "class Duck(Animal):\n", " def cry(self):\n", " print '꽥꽥'\n", " \n", "class Fish(Animal):\n", " pass\n", "\n", "for each in (Dog(), Duck(), Fish()):\n", " each.cry()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 2 내장 자료형과 클래스의 통일\n", "***\n", "- 내장 자료형(list, dict, tuple, string)을 상속하여 사용자 클래스를 정의하는 것\n", " - 내장 자료형과 사용자 자료형의 차이를 없에고 통일된 관점으로 모든 객체를 다룰 수 있는 방안\n", "- 클래스 정의는 새로운 자료형의 정의임" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-1 리스트 서브 클래스 만들기" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[]\n", "['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']\n" ] } ], "source": [ "a = list()\n", "print a\n", "print dir(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 아래 예제는 내장 자료형인 list를 상속하여 뺄셈 연산(-)을 추가함" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 'spam', 4, 5]\n", "\n", "[1, 2, 3, 5]\n" ] } ], "source": [ "class MyList(list):\n", " def __sub__(self, other): # '-' 연산자 중복 함수 정의\n", " for x in other:\n", " if x in self:\n", " self.remove(x) # 각 항목을 하나씩 삭제한다.\n", " return self\n", "\n", "L = MyList([1, 2, 3, 'spam', 4, 5])\n", "print L\n", "print\n", "\n", "L = L - ['spam', 4]\n", "print L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 1) Stack 클래스 정의 예\n", "- 슈퍼 클래스로 list 클래스를 지닌다.\n", "- 즉, list 클래스를 확장하여 Stack 클래스를 정의함" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4, 5]\n", "\n", "[1, 2, 3, 4, 5]\n", "\n", "5\n", "4\n", "[1, 2, 3]\n" ] } ], "source": [ "class Stack(list): # 클래스 정의\n", " push = list.append\n", " \n", "s = Stack() # 인스턴스 생성\n", "\n", "s.push(4)\n", "s.push(5)\n", "print s\n", "print\n", "\n", "s = Stack([1,2,3])\n", "s.push(4)\n", "s.push(5)\n", "print s\n", "print\n", "\n", "print s.pop() # 슈퍼 클래스인 리스트 클래스의 pop() 메소드 호출\n", "print s.pop()\n", "print s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 2) Queue 클래스 정의 예\n", "- 슈퍼 클래스로 역시 list를 지닌다.\n", "- 즉, list 클래스를 확장하여 Queue 클래스를 정의함 " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n", "1\n", "2\n" ] } ], "source": [ "class Queue(list):\n", " enqueue = list.append\n", " def dequeue(self):\n", " return self.pop(0)\n", " \n", "q = Queue()\n", "q.enqueue(1) # 데이터 추가\n", "q.enqueue(2)\n", "print q\n", "\n", "print q.dequeue() # 데이터 꺼내기\n", "print q.dequeue()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-2 사전 서브 클래스 만들기" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{}\n", "['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values', 'viewitems', 'viewkeys', 'viewvalues']\n" ] } ], "source": [ "a = dict()\n", "print a\n", "print dir(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 아래 예제는 keys() 메소드를 정렬된 키값 리스트를 반환하도록 재정의한다." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['one', 'three', 'two']\n", "\n", "['three', 'two', 'one']\n" ] } ], "source": [ "class MyDict(dict):\n", " def keys(self):\n", " K = dict.keys(self) # 언바운드 메소드 호출 --> K = self.keys() 라고 호출하면 무한 재귀 호출\n", " K.sort()\n", " return K\n", "\n", "d = MyDict({'one':1, 'two':2, 'three':3})\n", "print d.keys()\n", "print\n", "\n", "d2 = {'one':1, 'two':2, 'three':3}\n", "print d2.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 3 상속 관계에 있는 클래스들의 정보 획득\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3-1 객체가 어떤 클래스에 속해 있는지 확인하기" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체의 자료형 비교 방법 I (전통적 방법)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n" ] } ], "source": [ "import types\n", "\n", "print type(123) == types.IntType\n", "print type(123) == type(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체의 자료형 비교 방법 II (새로운 방법)\n", " - isinstance() 내장 함수와 기본 객체 클래스 사용" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " True\n", "\n" ] } ], "source": [ "print isinstance(123, int)\n", "print int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 서브 클래스의 인스턴스는 슈퍼 클래스의 인스턴스이기도 하다." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.A instance at 0x10de34e60> => A\n", "<__main__.B instance at 0x10de34e18> => B\n", "<__main__.C instance at 0x10de34cf8> => B C\n" ] } ], "source": [ "class A:\n", " pass\n", "\n", "class B:\n", " def f(self):\n", " pass\n", " \n", "class C(B):\n", " pass\n", "\n", "def check(obj):\n", " print obj, '=>',\n", " if isinstance(obj, A):\n", " print 'A',\n", " if isinstance(obj, B):\n", " print 'B',\n", " if isinstance(obj, C):\n", " print 'C',\n", " print\n", " \n", "a = A()\n", "b = B()\n", "c = C()\n", "\n", "check(a)\n", "check(b)\n", "check(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3-2 클래스 간의 상속 관계 알아내기\n", "- issubclass() 내장 함수 활용" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__main__.A => A\n", "__main__.B => B\n", "__main__.C => B C\n" ] } ], "source": [ "class A:\n", " pass\n", "\n", "class B:\n", " def f(self):\n", " pass\n", " \n", "class C(B):\n", " pass\n", "\n", "def check(obj):\n", " print obj, '=>',\n", " if issubclass(obj, A):\n", " print 'A',\n", " if issubclass(obj, B):\n", " print 'B',\n", " if issubclass(obj, C):\n", " print 'C',\n", " print\n", " \n", "check(A)\n", "check(B)\n", "check(C)" ] }, { "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.0" } }, "nbformat": 4, "nbformat_minor": 0 }