{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "***\n", "# 20. 클래스와 연산자 중복 정의\n", "***\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 1 연산자 중복 (Operator Overloading)\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-1 수치 연산자 중복\n", "- 직접 정의하는 클래스 인스턴스에 연산자를 적용하기 위하여 미리 약속되어 있는 메소드들을 정의" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "

메소드(Method)

연산자(Operator)

인스턴스 o에 대한 사용 예

\\_\\_add\\_\\_(self, B)

+ (이항)

o + B, o += B

\\_\\_sub\\_\\_(self, B)

- (이항)

o - B, o -= B

\\_\\_mul\\_\\_(self, B)

\\*

o \\* B, o \\*= B

\\_\\_div\\_\\_(self, B)

/

o / B, o /= B

\\_\\_floordiv\\_\\_(self, B)

//

o // B, o //= B

\\_\\_mod\\_\\_(self, B)

%

o % B, o %= B

\\_\\_divmod\\_\\_(self, B)

divmod()

divmod(o, B)

\\_\\_pow\\_\\_(self, B)

pow(), \\*\\*

pow(o, B), o \\*\\* B

\\_\\_lshift\\_\\_(self, B)

<<

o << B, o <<= B

\\_\\_rshift\\_\\_(self, B)

>>

o >> B, o >>= B

\\_\\_and\\_\\_(self, B)

&

o & B, o &= B

\\_\\_xor\\_\\_(self, B)

^

o ^ B, o ^= B

\\_\\_or\\_\\_(self, B) 

|

o | B, o |= B

\\_\\_neg\\_\\_(self)

- (단항)

-A

\\_\\_abs\\_\\_(self)

abs()

abs(o)

\\_\\_pos\\_\\_(self)

+ (단항)

+o

\\_\\_invert\\_\\_(self)

~

~o

" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "10\n", "\n", "20\n", "\n", "30\n" ] } ], "source": [ "class Integer:\n", " def __init__(self, i):\n", " self.i = i\n", " def __str__(self):\n", " return str(self.i)\n", " def __add__(self, other):\n", " return self.i + other\n", "\n", "i = Integer(10)\n", "print i\n", "print str(i)\n", "\n", "print\n", "i = i + 10\n", "print i\n", "\n", "print\n", "i += 10\n", "print i" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['abcd', 'abcd', 'abcd']\n", "['abcd', 'bcd', 'bcd']\n", "\n", "['abcd', 'abcd', 'abcd']\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", " def __div__(self, sep): # 나누기 연산자 /가 사용되었을 때 호출되는 함수 \n", " return self.str.split(sep) # 문자열 self.str을 sep를 기준으로 분리\n", "\n", "m = MyString(\"abcd_abcd_abcd\")\n", "print m / \"_\"\n", "print m / \"_a\"\n", "\n", "print\n", "print m.__div__(\"_\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 연산자 왼쪽에 피연산자, 연산자 오른쪽에 객체가 오는 경우\n", " - 메소드 이름 앞에 r이 추가된 메소드 정의" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['abcd', 'abcd', 'abcd']\n", "['abcd', 'bcd', 'bcd']\n", "\n", "['abcd', 'abcd', 'abcd']\n", "['abcd', 'bcd', 'bcd']\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", " def __div__(self, sep):\n", " return str.split(self.str, sep)\n", " __rdiv__ = __div__\n", "\n", "m = MyString(\"abcd_abcd_abcd\")\n", "print m / \"_\"\n", "print m / \"_a\"\n", "print\n", "print \"_\" / m\n", "print \"_a\" / m" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fedcba\n", "fedcba\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", " def __div__(self, sep):\n", " return str.split(self.str, sep)\n", " __rdiv__ = __div__\n", " def __neg__(self):\n", " t = list(self.str)\n", " t.reverse()\n", " return ''.join(t)\n", " __invert__ = __neg__\n", " \n", "m = MyString(\"abcdef\")\n", "print -m\n", "print ~m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1-2 비교 연산자 중복" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 각각의 비교 연산에 대응되는 메소드 이름이 정해져 있지만 그러한 메소드가 별도로 정의되어 있지 않으면 cmp가 호출됨\n", "\n", "|메소드 |연산자 | 비고 |\n", "|--------|----|----|\n", "|\\_\\_cmp\\_\\_(self, other) | 아래 메소드가 부재한 상황에 호출되는 메소드| |\n", "|\\_\\_lt\\_\\_(self, other) | self < other | |\n", "|\\_\\_le\\_\\_(self, other) | self <= other | |\n", "|\\_\\_eq\\_\\_(self, other) | self == other | |\n", "|\\_\\_ne\\_\\_(self, other) | self != other | |\n", "|\\_\\_gt\\_\\_(self, other) | self > other | |\n", "|\\_\\_ge\\_\\_(self, other) | self >= other | |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체 c에 대한 c > 1연산의 행동 방식\n", " - c.\\_\\_gt\\_\\_()가 있다면 호출 결과을 그대로 반환\n", " - 정의된 c.\\_\\_gt\\_\\_()가 없고, \\_\\_cmp\\_\\_() 함수가 있을 경우\n", " - c.\\_\\_cmp\\_\\_() 호출 결과가 양수이면 True 반환, 아니면 False 반환" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "False\n", "True\n" ] } ], "source": [ "class MyCmp:\n", " def __cmp__(self, y):\n", " return 1 - y\n", " \n", "c = MyCmp() \n", "print c > 1 # c.__cmp__(1)을 호출, 반환값이 양수이어야 True\n", "print c < 1 # c.__cmp__(1)을 호출, 반환값이 음수이어야 True\n", "print c == 1 # c.__cmp__(1)을 호출, 반환값이 0이어야 True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체 m에 대한 m < 10연산의 행동 방식\n", " - m.\\_\\_lt\\_\\_()가 있다면 호출 결과을 그대로 반환\n", " - 정의된 m.\\_\\_lt\\_\\_()가 없고, \\_\\_cmp\\_\\_() 함수가 있을 경우\n", " - m.\\_\\_cmp\\_\\_() 호출 결과가 음수이면 True 반환, 아니면 False 반환" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "False\n" ] } ], "source": [ "class MyCmp2:\n", " def __lt__(self, y):\n", " return 1 < y\n", "\n", "m = MyCmp2()\n", "print m < 10 # m.__lt__(10)을 호출\n", "print m < 2\n", "print m < 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체 m에 대한 m == 10연산의 행동 방식\n", " - m.\\_\\_eq\\_\\_()가 있다면 호출 결과을 그대로 반환\n", " - 정의된 m.\\_\\_eq\\_\\_()가 없고, \\_\\_cmp\\_\\_() 함수가 있을 경우\n", " - m.\\_\\_cmp\\_\\_() 호출 결과가 0이면 True 반환, 아니면 False 반환" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "True\n", "True\n" ] } ], "source": [ "class MyCmp3:\n", " def __eq__(self, y):\n", " return 1 == y\n", "\n", "m = MyCmp3()\n", "print m == 10 # m.__eq__(10)을 호출\n", "m1 = MyCmp3()\n", "print m == 1\n", "\n", "class MyCmp4:\n", " def __init__(self, value):\n", " self.value = value\n", " def __cmp__(self, other):\n", " if self.value == other:\n", " return 0\n", "m2 = MyCmp4(10)\n", "print m2 == 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 2 시퀀스/매핑 자료형의 연산자 중복\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스를 개발할 때 다음 메소드들을 적절하게 구현하면 자신만의 시퀀스 자료형을 만들 수 있음\n", "- 시퀀스 자료형 및 매핑 자료형을 위해 구현이 필요한 메소드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|메소드 |연산자 |\n", "|---|---|\n", "|\\_\\_len\\_\\_(self) | len() |\n", "|\\_\\_contains\\_\\_(self, item) | item in self |\n", "|\\_\\_getItem\\_\\_(self, key) | self[key] |\n", "|\\_\\_setItem\\_\\_(self, key, value) | self[key] = value |\n", "|\\_\\_delItem\\_\\_(self, key) | del self(key) |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-1 인덱싱 (Indexing)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- len(s1) --> s1.\\_\\_len\\_\\_() 메소드 호출\n", "- sl[4] --> s1.\\_\\_getitem\\_\\_(4) 호출\n", "- IndexError\n", " - 시퀀스 자료형이 범위를 벗어난 인덱스 참조 요구시에 발생시킴\n", " - 리스트, 튜플, 문자열등에서도 동일한 조건에서 발생됨" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "1\n", "16\n" ] }, { "ename": "IndexError", "evalue": "20", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\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 13\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0ms1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m#s1.__getitem__(1)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0ms1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0ms1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, k)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mk\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mend\u001b[0m \u001b[0;34m<=\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mk\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mIndexError\u001b[0m: 20" ] } ], "source": [ "class Square:\n", " def __init__(self, end):\n", " self.end = end\n", " def __len__(self):\n", " return self.end\n", " def __getitem__(self, k):\n", " if k < 0 or self.end <= k: \n", " raise IndexError, k\n", " return k * k\n", " \n", "s1 = Square(10)\n", "print len(s1) # s1.__len__()\n", "print s1[1] #s1.__getitem__(1)\n", "print s1[4]\n", "print s1[20]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 다음 for 문은 s1에 대해 \\_\\_getitem()\\_\\_ 메소드를 0부터 호출하여 IndexError가 발생하면 루프를 중단한다. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1 4 9 16 25 36 49 64 81\n" ] } ], "source": [ "for x in s1:\n", " print x," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_getitem\\_\\_() 메소드가 정의되어 있다면 다른 시퀀스 자료형으로 변환이 가능" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n", "(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)\n" ] } ], "source": [ "print list(s1)\n", "print tuple(s1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 위에서 알 수 있듯이 파이썬은 내장 자료형과 개발자가 정의한 자료형에 대해 일관된 연산 적용이 가능\n", " - 파이썬 언어의 장점: 일관된 코딩 스타일 유지" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2-2 매핑 자료형의 연산자 중복" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.MyDict instance at 0x103cc7ab8>\n", "light\n", "darkness\n", "2\n" ] } ], "source": [ "class MyDict:\n", " def __init__(self, d = None):\n", " if d == None: d = {}\n", " self.d = d\n", " def __getitem__(self, k): #key\n", " return self.d[k]\n", " def __setitem__(self, k, v):\n", " self.d[k] = v\n", " def __len__(self):\n", " return len(self.d)\n", " \n", "m = MyDict() #__init__호출\n", "m['day'] = 'light' #__setitem__호출\n", "m['night'] = 'darkness' #__setitem__호출 \n", "print m\n", "print m['day'] #__getitem__호출\n", "print m['night'] #__getitem__호출\n", "print len(m) #__len__호출" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['three', 'two', 'one']\n", "[3, 2, 1]\n", "[('three', 3), ('two', 2), ('one', 1)]\n" ] } ], "source": [ "class MyDict:\n", " def __init__(self, d=None):\n", " if d == None: d = {}\n", " self.d = d\n", " def __getitem__(self, k):\n", " return self.d[k]\n", " def __setitem__(self, k, v):\n", " self.d[k] = v\n", " def __len__(self):\n", " return len(self.d)\n", " def keys(self):\n", " return self.d.keys()\n", " def values(self):\n", " return self.d.values()\n", " def items(self):\n", " return self.d.items()\n", " \n", "m = MyDict({'one':1, 'two':2, 'three':3})\n", "print m.keys()\n", "print m.values()\n", "print m.items()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "## 3 문자열 변환과 호출 가능 객체\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3-1 문자열로 변환하기\n", "1) \\_\\_repr\\_\\_\n", "- 객체를 대표하여 유일하게 표현할 수 있는 공식적인 문자열\n", "- eval() 함수에 의하여 같은 객체로 재생성 될 수 있는 문자열 표현\n", "\n", "2) \\_\\_str\\_\\_\n", "- 객체의 비공식적인 문자열 표현\n", "- 사용자가 보기 편한 형태로 자유롭게 표현될 수 있음" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str called\n", "str called\n", "repr called\n", "repr called\n" ] } ], "source": [ "class StringRepr:\n", " def __repr__(self):\n", " return 'repr called'\n", " def __str__(self):\n", " return 'str called'\n", " \n", "s = StringRepr()\n", "print s\n", "print str(s)\n", "print repr(s)\n", "print `s`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_str\\_\\_() 호출시\n", " - \\_\\_str\\_\\_()가 정의되어 있지 않으면 \\_\\_repr\\_\\_()이 대신 호출됨" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "repr called\n", "repr called\n", "repr called\n", "repr called\n" ] } ], "source": [ "class StringRepr:\n", " def __repr__(self):\n", " return 'repr called'\n", " \n", "s = StringRepr()\n", "print s\n", "print repr(s)\n", "print str(s)\n", "print `s`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_repr\\_\\_() 호출시\n", " - \\_\\_repr\\_\\_()이 정의되어 있지 않으면 객체 식별자가 출력됨\n", " - 대신하여 \\_\\_str\\_\\_()이 호출되지 않음" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str called\n", "<__main__.StringRepr instance at 0x103cc7f80>\n", "str called\n", "<__main__.StringRepr instance at 0x103cc7f80>\n" ] } ], "source": [ "class StringRepr:\n", " def __str__(self):\n", " return 'str called'\n", "\n", "s = StringRepr()\n", "print s\n", "print repr(s)\n", "print str(s)\n", "print `s`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3-2 호출 가능한 클래스 인스턴스 만들기\n", "- 클래스 인스턴스에 \\_\\_call\\_\\_ 메소드가 구현되어 있다면 해당 인스턴스는 함수와 같이 호출될 수 있다.\n", " - 인스턴스 x에 대해 x(a1, a2, a3)와 같이 호출된다면 x.\\_\\_call\\_\\_(a1, a2, a3)가 호출된다." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "15\n", "21\n", "45\n", "45\n" ] } ], "source": [ "class Accumulator:\n", " def __init__(self):\n", " self.sum = 0\n", " def __call__(self, *args):\n", " self.sum += sum(args)\n", " return self.sum\n", " \n", "acc = Accumulator()\n", "print acc(1,2,3,4,5)\n", "print acc(6)\n", "print acc(7,8,9)\n", "print acc.sum" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "15\n", "6\n", "24\n", "24\n" ] } ], "source": [ "class Accumulator:\n", " def __init__(self):\n", " self.sum = 0\n", " def __call__(self, *args):\n", " self.sum = reduce(lambda x, y: x + y, args)\n", " return self.sum\n", " \n", "acc = Accumulator()\n", "print acc(1,2,3,4,5)\n", "print acc(6)\n", "print acc(7,8,9)\n", "print acc.sum" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 호출 가능 객체인지 알아보기" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "callable\n", "not callable\n", "\n", "True\n", "False\n" ] } ], "source": [ "def check(obj):\n", " if callable(obj):\n", " print 'callable'\n", " else:\n", " print 'not callable'\n", "\n", "class B:\n", " def func(self, v):\n", " return v\n", " \n", "class A:\n", " def __call__(self, v):\n", " return v\n", " \n", "a = A()\n", "b = B()\n", "check(a)\n", "check(b)\n", "print\n", "print callable(a)\n", "print callable(b)" ] }, { "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 }