{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 9. 클래스와 매직 메서드\n", "- 클래스는 **사용자 정의 타입(user-defined type)**을 작성하는 능력을 제공함\n", "- 파이썬 클래스\n", " - 단순 (Simple)\n", " - 독립적인 이름 공간 (Name Space) 제공\n", " - 어떤 데이터를 보유할 수 있는지에 대한 명세\n", " - 어떤 기능을 할 수 있는지에 대한 명세\n", " - 메직 메소드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[들어가기 전에] 전역 변수 (Global Variable)과 이름 공간" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 전역 변수 1개" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "result = 0\n", "\n", "def add(num):\n", " global result\n", " result += num\n", "\n", "add(3)\n", "add(4)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "ename": "UnboundLocalError", "evalue": "local variable 'result' referenced before assignment", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/3474516729.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mnum\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0madd_a\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/3474516729.py\u001b[0m in \u001b[0;36madd_a\u001b[0;34m(num)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0madd_a\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mnum\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0madd_a\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'result' referenced before assignment" ] } ], "source": [ "def add_a(num):\n", " result += num\n", "\n", "add_a(5)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "def add_b(num):\n", " result = 7\n", " result += num\n", "\n", "add_b(5)" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 전역 변수 2개" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "result1 = 0\n", "result2 = 0\n", "\n", "def add1(num):\n", " global result1\n", " result1 += num\n", "\n", "def add2(num):\n", " global result2\n", " result2 += num\n", "\n", "add1(3)\n", "add1(4)\n", "\n", "add2(3)\n", "add2(7)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result1" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스를 사용한 코드 개선" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [], "source": [ "class Calculator:\n", " def __init__(self):\n", " self.result = 0\n", "\n", " def add(self, num):\n", " self.result += num\n", "\n", "cal1 = Calculator()\n", "cal2 = Calculator()\n", "\n", "cal1.add(3)\n", "cal1.add(4)\n", "cal2.add(3)\n", "cal2.add(7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.1 클래스와 객체 기본 문법" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Car:\n", " pass" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Dog:\n", " pass\n", "\n", "class Cat:\n", " pass" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "car1 = Car()\n", "car2 = Car()\n", "car3 = Car()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "my_dog = Dog()\n", "yr_dog = Dog()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스 변수(class variable) 정의\n", " - 클래스 변수는 해당 클래스로 부터 생성되는 모든 객체가 공유함" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Car:\n", " accel = 3.0\n", " mpg = 25" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "car1.accel = 3.0\n", "car2.accel = 3.0\n", "car1.mpg = 25\n", "car2.mpg = 25\n" ] } ], "source": [ "car1 = Car()\n", "car2 = Car()\n", "\n", "print('car1.accel = ', car1.accel)\n", "print('car2.accel = ', car2.accel)\n", "print('car1.mpg = ', car1.mpg)\n", "print('car2.mpg = ', car2.mpg)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "car1.accel = 3.0\n", "car2.accel = 3.0\n", "car1.mpg = 25\n", "car2.mpg = 25" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "my_car = Car()\n", "yr_car = Car()\n", "my_car.accel = 5.0" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.0\n", "3.0\n" ] } ], "source": [ "print(my_car.accel)\n", "print(yr_car.accel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.2 인스턴스 변수에 대해 더 알아보자" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Dog:\n", " pass\n", "\n", "my_dog = Dog() \t\t\t # Dog 인스턴스 생성\n", "my_dog.name = 'Champ the Wonder Dog'\n", "my_dog.breed = 'Great Dane'\n", "my_dog.age = 5" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Breed and age are Great Dane and 5.\n" ] } ], "source": [ "print('Breed and age are {} and {}.'.format(my_dog.breed, my_dog.age))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "yr_dog = Dog()\n", "top_dog = Dog()\n", "hot_dog = Dog()\n", "hot_dog.name = 'Hotty Totty'\n", "hot_dog.breed = 'Dachshund'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [파이썬 클래스와 이름 공간]\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": 11, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": 15, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": 16, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": 17, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Simple:\n", " pass\n", "\n", "s1 = Simple()\n", "s2 = Simple()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "pycharm": { "name": "#%%\n" } }, "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/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/2042994929.py\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[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[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[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\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을 정의한 적이 없다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](images/instance.png)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "del s1.stack # s1에서 stack삭제" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [클래스 멤버와 인스턴스 멤버]\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스 멤버 vs. 인스턴스 멤버\n", " - 클래스 멤버\n", " - 클래스 이름 공간에 생성됨\n", " - 모든 인스턴스들에 의해 공유됨\n", " - 인스턴스 멤버\n", " - 인스턴스 이름 공간에 생성됨\n", " - 각각의 인스턴스 마다 독립성이 보장됨" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": "markdown", "metadata": {}, "source": [ "![inheritance](images/instance2.png)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n" ] } ], "source": [ "print(Var.c_mem) # 클래스를 통하여 클래스 멤버 접근" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n" ] } ], "source": [ "v1 = Var() # 인스턴스 v1 생성\n", "print(v1.c_mem) # 인스턴스를 통하여 클래스 멤버 접근" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "200\n" ] } ], "source": [ "v1.f() # 인스턴스 멤버 i_mem이 생성됨\n", "print(v1.i_mem) # 인스턴스 v1을 통하여 인스턴스 멤버 접근" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "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/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/3624617570.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\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 2\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----> 3\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": [ "v2 = Var() # 인스턴스 v2 생성\n", "print(v2.c_mem)\n", "print(v2.i_mem) # 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \"인스턴스 이름.멤버 이름\"으로 멤버를 참조할 때 멤버의 검색 순서\n", " - 1) 인스턴스 멤버\n", " - 2) 인스턴스 멤버가 없다면 클래스 멤버" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": [ " ## 9.3 \\_\\_init\\_\\_ 메서드와 \\_\\_new\\_\\_ 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_init\\_\\_()\n", " - 생성하고 있는 객체의 초기화 역할\n", " - self\n", " " ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Dog:\n", " def __init__(self, name, breed, age):\n", " self.name = name\n", " self.breed = breed\n", " self.age = age" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "top_dog = Dog('Handsome Dan', 'Bulldog', 10)\n", "\n", "#아래 3줄은 위 코드와 동일함\n", "top_dog.name = 'Handsome Dan'\n", "top_dog.breed = 'Bulldog'\n", "top_dog.age = 10" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "good_dog = Dog('WonderBoy', 'Collie', 11)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_new\\_\\_()\n", " - 객체에 메모리를 할당(Allocate)하는 것은 \\_\\_new\\_\\_() 메소드\n", " - 파이썬에서 객체를 생성할 때 \\_\\_init\\_\\_()이 실행되기 직전에 항상 \\_\\_new\\_\\_()가 먼저 실행되며 이 때 객체에 메모리가 할당돰" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [생성자와 소멸자]\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": 26, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Birthday Mon Nov 15 21:02:14 2021\n", "Sleeping for 3 sec\n", "Deathday Mon Nov 15 21:02:17 2021\n" ] } ], "source": [ "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": 43, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", " \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": [ "## 9.4 클래스와 선행 참조(Forward Reference) 문제" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "ename": "NameError", "evalue": "name 'Person' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/461443531.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhusband\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'm'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0ma_marriage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Person 클래스 초기화 누락\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/461443531.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwife\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'f'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhusband\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'm'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mNameError\u001b[0m: name 'Person' is not defined" ] } ], "source": [ "# 문제가 발생하는 코드\n", "class Marriage:\n", " def __init__(self):\n", " self.wife = Person('f')\n", " self.husband = Person('m')\n", "\n", "a_marriage = Marriage() # Person 클래스 초기화 누락\n", "\n", "class Person:\n", " def __init__(self, gender):\n", " self.gender = gender" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "# 문제가 발생하는 코드 수정\n", "class Marriage:\n", " def __init__(self):\n", " self.wife = Person('f')\n", " self.husband = Person('m')\n", "\n", "class Person:\n", " def __init__(self, gender):\n", " self.gender = gender\n", " \n", "a_marriage = Marriage() # 해당 라인을 끝으로 옮김" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "ename": "RecursionError", "evalue": "maximum recursion depth exceeded while calling a Python object", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/937282721.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmarriage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0ma_marriage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 해당 라인을 끝으로 옮김\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/937282721.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwife\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'f'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhusband\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'm'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/937282721.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, gender)\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgender\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgender\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgender\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmarriage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0ma_marriage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 해당 라인을 끝으로 옮김\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "... last 2 frames repeated, from the frame below ...\n", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/937282721.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mMarriage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwife\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'f'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhusband\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'm'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded while calling a Python object" ] } ], "source": [ "# 문제가 발생하는 코드 수정\n", "class Marriage:\n", " def __init__(self):\n", " self.wife = Person('f')\n", " self.husband = Person('m')\n", "\n", "class Person:\n", " def __init__(self, gender):\n", " self.gender = gender\n", " self.marriage = Marriage()\n", " \n", "a_marriage = Marriage() # 해당 라인을 끝으로 옮김" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 선행 참조에 관한 주요 명심 사항\n", " - 모든 클래스들은 인스턴스화 하기 전에 미리 정의되어 있어야 함\n", " - 클래스 상호 간에 인스턴스화를 하는 것(Mutual dependency)은 금지" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.5 메서드 기본" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Pretty:\n", " def __init__(self, prefix):\n", " self.prefix = prefix\n", "\n", " def print_me(self, a, b, c):\n", " print(self.prefix, a, sep='')\n", " print(self.prefix, b, sep='')\n", " print(self.prefix, c, sep='')\n", "\n", " def print_class(): # 클래스 범위 메소드 (객체로 부터 호출 불가)\n", " print(\"Preedy class\")" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-->10\n", "-->20\n", "-->30\n" ] } ], "source": [ "printer = Pretty('-->')\n", "printer.print_me(10, 20, 30) # 바운드 메소드 호출 (Bound Method Call)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-->10\n", "-->20\n", "-->30\n" ] } ], "source": [ "Pretty.print_me(printer, 10, 20, 30) # 언바운드 메소드 호출 ( Unbound Method Call)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Preedy class\n" ] } ], "source": [ "Pretty.print_class() # 클래스 범위 메소드 (객체로 부터 호출 불가)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "print_class() takes 0 positional arguments but 1 was given", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: print_class() takes 0 positional arguments but 1 was given" ] } ], "source": [ "printer.print_class()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.6 전역 변수/메서드와 지역 변수/메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - 위 제목에서 \"전역 변수/메소드\"는 삭제하고 공부하자!!!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 파이썬에서 객체 지향 설계는 매우 단순!\n", " - Capsulization (캡슐화)와 대치\n", " - 파이썬에는 public, package, private 등의 키워드 없음\n", " - 클래스 내 존재하는 모든 것이 public\n", " - 한가지 예외: Mangled Member <-- 클래스 내부에서만 접근 가능한 **지역 변수/메소드**" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Odd:\n", " def __init__(self):\n", " self.x = 10\n", " self.y = 20\n", " self._u = 30 # 클래스 정의 내부에서만 사용하는 특수 변수라는 약속. 외부에서 직접 접근하는 것을 하지 말자는 약속. 접근은 허용됨\n", " self.__z = 40 # Mangled Member --> 외부에서 접근 불가 ([주의] 메직 메소드 아님!!!)\n", "\n", " def pr(self):\n", " print('__z = ', self.__z)\n", " \n", " def __pr(self):\n", " print(\"!!!!!!!!!!!!!\")" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o = Odd()\n", "o.x \t\t# 10" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o.y \t\t# 20" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "o._u " ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "ename": "AttributeError", "evalue": "'Odd' object has no attribute '__z'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/1459712312.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__z\u001b[0m \u001b[0;31m# 에러!\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'Odd' object has no attribute '__z'" ] } ], "source": [ "o.__z \t\t# 에러!" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__z = 40\n" ] } ], "source": [ "o.pr()" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "'Odd' object has no attribute '__pr'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/0z/7y5lq_xs3kv2s9p43vjt16bh0000gn/T/ipykernel_67644/614610455.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__pr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'Odd' object has no attribute '__pr'" ] } ], "source": [ "o.__pr()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.7 상속(Inheritance)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 클래스 상속과 이름 공간의 관계" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 상속의 이유\n", " - 코드의 재사용\n", " - 상속받은 자식 클래스는 상속을 해준 부모 클래스의 모든 기능을 그대로 사용\n", " - 자식 클래스는 필요한 기능만을 정의하거나 기존의 기능을 변경(재정의, Override)할 수 있음\n", "\n", "- 부모 클래스 메소드 호출 방법\n", " - Unbound 메소드 호출\n", " - 부모 클래스.메소드(self, ...)\n", " - Bound 메소드 호출 (더 자주 이용됨)\n", " - ```super(Subclass, self)``` \n", " - super()에 대한 자세한 설명\n", " - https://realpython.com/python-super/" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Person:\n", " def __init__(self, name, phone=None):\n", " self.name = name\n", " self.phone = phone\n", "\n", " def __str__(self):\n", " return ''.format(self.name, self.phone)" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Employee(Person): # 괄호 안에 쓰여진 클래스는 슈퍼클래스를 의미한다.\n", " def __init__(self, name, phone, position, salary):\n", " #Person.__init__(self, name, phone) # Person클래스의 생성자 호출 --> Unbound Method Call\n", " super(Employee, self).__init__(name, phone) # --> Bound Method Call\n", "\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": 96, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": "code", "execution_count": 97, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['name', 'phone']\n" ] } ], "source": [ "l1 = [x for x in dir(p1) if not x.startswith(\"__\")]\n", "print(l1)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['name', 'phone', 'position', 'salary']\n" ] } ], "source": [ "l2 = [x for x in dir(m1) if not x.startswith(\"__\")]\n", "print(l2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](images/inheritance3.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 생성자 호출\n", "- 서브 클래스의 생성자는 슈퍼 클래스의 생성자를 자동으로 호출하지 않는다." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": 100, "metadata": { "pycharm": { "name": "#%%\n" } }, "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) # Unbound 방식으로 슈퍼클래스의 생성자를 호출한다.\n", " super(Sub, self).__init__() # 또는 왼쪽처럼 Bound 메소드 호출\n", " print('Sub init called')\n", "\n", "s = Sub()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 서브 클래스에 생성자가 정의되어 있지 않은 경우에는 슈퍼 클래스의 생성자가 호출된다." ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": [ "#### 메쏘드의 대치 (메소드 오버라이드 - Override)\n", "- 서브 클래스에서 슈퍼 클래스에 정의된 메소드를 재정의하여 대치하는 기능" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", "\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", " super(Employee, self).__init__(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": 103, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", " super(Employee, self).__init__(name, phone)\n", " self.position = position\n", " self.salary = salary\n", "\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": "code", "execution_count": 104, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", " super(Employee, self).__init__(name, phone)\n", " self.position = position\n", " self.salary = salary\n", "\n", " def __str__(self):\n", " s = super().__str__() # 부모 객체의 메소드 호출 (bound)\n", " s = s.replace(\"Person\", \"Employee\")\n", " return s + ' - <%s %s>' % (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": "code", "execution_count": 105, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", " super(Employee, self).__init__(name, phone)\n", " self.position = position\n", " self.salary = salary\n", "\n", " def __str__(self):\n", " s = Person.__str__(self) # 부모 객체의 메소드 호출 (unbound)\n", " s = s.replace(\"Person\", \"Employee\")\n", " return s + ' - <%s %s>' % (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": [ "#### 다형성(Polymorphism)\n", "- 상속 관계 내의 다른 클래스들의 인스턴스들이 같은 멤버 함수 호출에 대해 각각 다르게 반응하도록 하는 기능\n", " - 연산자 오버로딩(Operator Overloading)도 다형성과 관련된 중요한 기술\n", " - 연산자 오버로딩: a와 b의 객체 형에 따라 a + b의 + 연산자 행동 방식이 변경되는 것\n", "\n", "\n", "- 다형성의 장점\n", " - 적은 코딩으로 다양한 객체들에게 유사한 작업을 수행시킬 수 있음\n", " - 일관된 코딩 방식이 유지됨\n", " - 프로그램 작성 코드 량이 줄어든다.\n", " - 코드의 가독성을 높혀준다\n", "\n", "\n", "- 파이썬에서 다형성의 장점\n", " - 형 선언이 없다는 점에서 파이썬에서는 다형성을 적용하기가 더욱 용이하다.\n", " - 실시간으로 객체의 형이 결정 => 하나의 메소드에 연관시킬 수 있는 객체의 종류에 제한이 없다.\n", " - 즉, 상속 관계등의 클래스 간의 관계 설정 필요 없이 다형성 적용 가능" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", "### [내장 자료형과 클래스의 통일]\n", "***\n", "- 내장 자료형(list, dict, tuple, string)을 상속하여 사용자 클래스를 정의하는 것\n", " - 내장 자료형과 사용자 자료형의 차이를 없에고 통일된 관점으로 모든 객체를 다룰 수 있는 방안\n", "- 클래스 정의는 새로운 자료형의 정의임" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 리스트 서브 클래스 만들기" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[]\n", "['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', '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": 36, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": 79, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", " def push(self, other):\n", " self.append(other)\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": "code", "execution_count": 78, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": "markdown", "metadata": {}, "source": [ "![inheritance](images/queue.png)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n", "1\n", "2\n" ] } ], "source": [ "class Queue(list):\n", " def enqueue(self, obj): \n", " self.append(obj)\n", " \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": "code", "execution_count": 34, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n", "1\n", "2\n" ] } ], "source": [ "class Queue(list):\n", " enqueue = list.append\n", "\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": [ "#### 사전 서브 클래스 만들기" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{}\n", "['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']\n" ] } ], "source": [ "a = dict()\n", "print(a)\n", "print(dir(a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 아래 예제는 keys() 메소드를 정렬된 키값 리스트를 반환하도록 재정의한다." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['b', 'c', 'a'])\n", "['b', 'c', 'a']\n", "\n", "['a', 'b', 'c']\n" ] } ], "source": [ "class MyDict(dict):\n", " def keys(self):\n", " K = list(dict.keys(self)) # 언바운드 메소드 호출 --> K = list(self.keys()) 라고 호출하면 무한 재귀 호출\n", " K.sort()\n", " return K\n", "\n", "d = {'b':1, 'c':2, 'a':3}\n", "print(d.keys())\n", "print(list(d.keys()))\n", "print()\n", "\n", "d2 = MyDict({'b':1, 'c':2, 'a':3})\n", "print(d2.keys())" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['b', 'c', 'a'])\n", "['b', 'c', 'a']\n", "\n", "['a', 'b', 'c']\n" ] } ], "source": [ "class MyDict(dict):\n", " def keys(self):\n", " K = list(super().keys()) # 바운드 메소드 호출\n", " K.sort()\n", " return K\n", "\n", "d = {'b':1, 'c':2, 'a':3}\n", "print(d.keys())\n", "print(list(d.keys()))\n", "print()\n", "\n", "d2 = MyDict({'b':1, 'c':2, 'a':3})\n", "print(d2.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [상속 관계에 있는 클래스들의 정보 획득]\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 객체가 어떤 클래스에 속해 있는지 확인하기" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체의 자료형 비교 방법 I (전통적 방법)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "True\n" ] } ], "source": [ "print(type(123) == int)\n", "print(type(123) == type(0))\n", "a = 12345678\n", "print(type(a) == type(0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 객체의 자료형 비교 방법 II (새로운 방법)\n", " - isinstance() 내장 함수와 기본 객체 클래스 사용" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "print(isinstance(123, int))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 서브 클래스의 인스턴스는 슈퍼 클래스의 인스턴스이기도 하다." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.A object at 0x7fa2b8111710> => A\n", "<__main__.B object at 0x7fa2b03d34a8> => B\n", "<__main__.C object at 0x7fa2b8111080> => BC\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, '=>', end=\" \")\n", " if isinstance(obj, A):\n", " print('A', end=\"\")\n", " if isinstance(obj, B):\n", " print('B', end=\"\")\n", " if isinstance(obj, C):\n", " print('C', end=\"\")\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": [ "#### 클래스 간의 상속 관계 알아내기\n", "- issubclass() 내장 함수 활용" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " => A\n", " => B\n", " => BC\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(class_obj):\n", " print(class_obj, '=>', end=\" \")\n", " if issubclass(class_obj, A):\n", " print('A', end=\"\")\n", " if issubclass(class_obj, B):\n", " print('B', end=\"\")\n", " if issubclass(class_obj, C):\n", " print('C', end=\"\")\n", " print()\n", "\n", "check(A)\n", "check(B)\n", "check(C)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## 9.8 다중 상속(Multiple Inheritance)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Mammal:\n", " def __init__(self, name, size):\n", " self.name = name\n", " self.size = size\n", "\n", " def speak(self):\n", " print(\"My name is\", self.name)\n", " \n", " def call_out(self):\n", " self.speak()\n", " self.speak()\n", " self.speak()\n", " \n", "class Pet:\n", " pass\n", "\n", "class Carnivore:\n", " pass\n", "\n", "class Dog(Mammal, Pet, Carnivore):\n", " def __init__(self, name, size, breed):\n", " Mammal.__init__(self, name, size) # 원하는 상위 클래스 생성자만 선택하여 호출\n", " self.breed = breed\n", "\n", " def speak(self):\n", " print('ARF!')" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ARF!\n", "10\n", "##########\n", "ARF!\n", "ARF!\n", "ARF!\n" ] } ], "source": [ "dog = Dog(\"jack\", 10, True)\n", "dog.speak()\n", "print(dog.size)\n", "print(\"#\" * 10)\n", "dog.call_out()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Pet:\n", " def __init__(self, nickname):\n", " self.nickname = nickname\n", "\n", "class Dog(Mammal, Pet, Carnivore):\n", " def __init__(self, name, size, nickname, breed):\n", " Mammal.__init__(self, name, size) # 원하는 상위 클래스 생성자만 선택하여 호출\n", " Pet.__init__(self, nickname) # 원하는 상위 클래스 생성자만 선택하여 호출\n", " self.breed = breed\n", "\n", " def speak(self):\n", " print('ARF!')" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ARF!\n", "jackjack\n", "##########\n", "ARF!\n", "ARF!\n", "ARF!\n" ] } ], "source": [ "dog = Dog(\"jack\", 10, \"jackjack\", True)\n", "dog.speak()\n", "print(dog.nickname)\n", "print(\"#\" * 10)\n", "dog.call_out()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "- [보충 자료] 다중 상속\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![inheritance](images/MRO.jpg)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class X: pass\n", "class Y: pass\n", "class Z: pass\n", "\n", "class A(X, Y): pass\n", "class B(Y, Z): pass\n", "\n", "class M(B, A, Z): pass" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "\n", "False\n", "False\n", "\n", "[, , , ]\n" ] } ], "source": [ "x = X()\n", "y = Y()\n", "z = Z()\n", "\n", "a = A()\n", "b = B()\n", "\n", "m = M()\n", "\n", "print(issubclass(A, X))\n", "print(isinstance(a, X))\n", "\n", "print()\n", "\n", "print(issubclass(B, X))\n", "print(isinstance(b, X))\n", "\n", "print()\n", "\n", "print(B.mro())" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "\n", "True\n", "True\n", "\n", "True\n", "True\n" ] } ], "source": [ "print(issubclass(M, Z))\n", "print(isinstance(m, Z))\n", "\n", "print()\n", "\n", "print(issubclass(M, X))\n", "print(isinstance(m, X))\n", "\n", "print()\n", "\n", "print(issubclass(M, Y))\n", "print(isinstance(m, Y))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[, , , , , , ]\n" ] } ], "source": [ "print(M.mro()) # method resolution order" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 다중 상속 예제" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Alice\n", "3\n", "10\n" ] } ], "source": [ "class Human:\n", " def __init__(self, name):\n", " self.name = name\n", "\n", "class Coder:\n", " def __init__(self, skills):\n", " self.skills = skills\n", "\n", "class Pythonista(Human, Coder):\n", " def __init__(self, name, skills, dream):\n", " Human.__init__(self, name)\n", " Coder.__init__(self, skills)\n", " self.dream = dream\n", "\n", "obj = Pythonista(\"Alice\", 3, 10)\n", "\n", "print(obj.name)\n", "print(obj.skills)\n", "print(obj.dream)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 다중 상속시 super() 사용 예제 [생략]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Class D __init__()\n", "Class B __init__()\n", "Class A __init__()\n", "Class C __init__()\n", "Class A __init__()\n" ] } ], "source": [ "class A:\n", " def __init__(self):\n", " print(\"Class A __init__()\")\n", "\n", "class B(A):\n", " def __init__(self):\n", " print(\"Class B __init__()\")\n", " A.__init__(self)\n", "\n", "class C(A):\n", " def __init__(self):\n", " print(\"Class C __init__()\")\n", " A.__init__(self)\n", "\n", "class D(B, C):\n", " def __init__(self):\n", " print(\"Class D __init__()\")\n", " B.__init__(self)\n", " C.__init__(self)\n", "\n", "d = D()" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Class D __init__()\n", "Class B __init__()\n", "Class C __init__()\n", "Class A __init__()\n", "[, , , , ]\n" ] } ], "source": [ "class A:\n", " def __init__(self):\n", " print(\"Class A __init__()\")\n", "\n", "class B(A):\n", " def __init__(self):\n", " print(\"Class B __init__()\")\n", " super().__init__()\n", "\n", "class C(A):\n", " def __init__(self):\n", " print(\"Class C __init__()\")\n", " super().__init__()\n", "\n", "class D(B, C):\n", " def __init__(self):\n", " print(\"Class D __init__()\")\n", " super().__init__()\n", "\n", "d = D()\n", "print(D.mro())" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Class D __init__()\n", "Class B __init__()\n", "Class C __init__()\n", "Class A __init__()\n", "[, , , , ]\n" ] } ], "source": [ "class A:\n", " def __init__(self):\n", " print(\"Class A __init__()\")\n", "\n", "class B(A):\n", " def __init__(self):\n", " print(\"Class B __init__()\")\n", " super(B, self).__init__()\n", "\n", "class C(A):\n", " def __init__(self):\n", " print(\"Class C __init__()\")\n", " super(C, self).__init__()\n", "\n", "class D(B, C):\n", " def __init__(self):\n", " print(\"Class D __init__()\")\n", " super(D, self).__init__()\n", "\n", "d = D()\n", "print(D.mro())" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## 9.9 매직 메서드 개요" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.10 매직 메서드 상세\n", "\n", "### 9.10.1 파이썬 클래스의 문자열 표현" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 위 그림에서 '기본 호출'의 의미는 해당 메서드의 내용이 명시적으로 구현되어 있지 않을 때 기본적으로(Default) 호출한다는 의미" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "'110'" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(6, 'b')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.2 객체 표현 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "class A:\n", " pass\n", "\n", "a = A()" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<__main__.A at 0x7fa2a05846a0>" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'<__main__.A object at 0x7fa2a05846a0>'" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.__str__()" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'<__main__.A object at 0x7fa2a05846a0>'" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.__repr__()" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8771028223082" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.__hash__()" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " big_prime_1 = 1200556037\n", " big_prime_2 = 2444555677\n", "\n", " def __init__(self, x = 0, y = 0):\n", " self.x = x\n", " self.y = y\n", "\n", " def __str__(self):\n", " s = str(self.x) + ', '\n", " s += str(self.y)\n", " return s\n", "\n", " def __repr__(self):\n", " s = 'Point(' + str(self.x) + ', '\n", " s += str(self.y) + ')'\n", " return s\n", "\n", " def __hash__(self):\n", " n = self.x * big_prime_1\n", " return (n + self.y) % big_prime_2\n", "\n", "# def __bool__(self):\n", "# return x and y\n", " \n", " def __bool__(self):\n", " if x == y:\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "Point(3, 4)" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pt = Point(3, 4)\n", "pt" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3, 4\n" ] } ], "source": [ "print(pt)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bool(pt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.3 비교 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Dog:\n", " def __init__(self, n):\n", " self.n = n\n", "\n", " def __eq__(self, other):\n", " ''' == 를 구현하면 != 를 무료로 얻는다.'''\n", " return self.n == other.n\n", "\n", " def __lt__(self, other):\n", " '' '< 를 구현하면 > 를 무료로 얻는다.'''\n", " return self.n < other.n\n", "\n", " def __le__(self, other):\n", " ''' <= 를 구현하면, >= 를 무료로 얻는다.'''\n", " return self.n <= other.n" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "a = Dog(10)\n", "b = Dog(100)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False True\n" ] } ], "source": [ "print(a == b, a != b)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True False\n" ] } ], "source": [ "print(a < b, a > b)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True False\n" ] } ], "source": [ "print(a <= b, a >= b)" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Dog:\n", " def __init__(self, d):\n", " self.d = d\n", "\n", " def __gt__(self, other):\n", " ''' \n", " Greater than (>). \n", " '''\n", " print(\"__gt__() is called !!!!!!!!!!!\") \n", " if type(other) == Dog:\n", " return self.d > other.d\n", " else:\n", " return self.d > other\n", "\n", " def __lt__(self, other):\n", " ''' \n", " Less than (<). \n", " '''\n", " print(\"__lt__() is called !!!!!!!!!!!\")\n", " if type(other) == Dog:\n", " return self.d < other.d\n", " else:\n", " return self.d < other\n", "\n", " # _ _repr_ _ 정의하는 것은 _ _str_ _도 갖게 되는 것이다.\n", " def __repr__(self):\n", " return \"Dog(\" + str(self.d) + \")\"\n" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "d1 = Dog(1)\n", "d2 = Dog(10)\n", "d3 = 2" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__lt__() is called !!!!!!!!!!!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d1 < d2" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__lt__() is called !!!!!!!!!!!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d1 < d3" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__lt__() is called !!!!!!!!!!!\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d2 < d1" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__gt__() is called !!!!!!!!!!!\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d3 < d1" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog(1)\n" ] } ], "source": [ "print(d1)" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__lt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n", "__lt__() is called !!!!!!!!!!!\n", "__lt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n", "__lt__() is called !!!!!!!!!!!\n", "__lt__() is called !!!!!!!!!!!\n", "__lt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n", "__gt__() is called !!!!!!!!!!!\n" ] } ], "source": [ "d1, d5, d10 = Dog(1), Dog(5), Dog(10)\n", "a_list = [50, d5, 100, d1, -20, d10, 3]\n", "a_list.sort()" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "[-20, Dog(1), 3, Dog(5), Dog(10), 50, 100]" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.4 산술(Arithmetic) 연산자 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3/2\n", "5/2\n" ] } ], "source": [ "import fractions\n", "\n", "f = fractions.Fraction(1, 2)\n", "print(f + 1)\t\t\t # Fraction._ _add_ _ 호출\n", "print(2 + f) \t\t\t # Fraction._ _radd_ _ 호출" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", "\n", " def __add__(self, other):\n", " ''' self+other를 포함한 포인트 반환.'''\n", " newx = self.x + other.x\n", " newy = self.y + other.y\n", " return Point(newx, newy)\n", "\n", " def __sub__(self, other):\n", " ''' 두 포인트의 거리 반환.'''\n", " dx = self.x - other.x\n", " dy = self.y - other.y\n", " return (dx * dx + dy * dy) ** 0.5\n", "\n", " def __mul__(self, n):\n", " ''' point 곱하기 스칼라 숫자 n.'''\n", " newx = self.x * n\n", " newy = self.y * n\n", " return Point(newx, newy)\n", " \n", " def __str__(self):\n", " return \"Point({0}, {1})\".format(self.x, self.y)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "pt1 = Point(10, 15)\n", "pt2 = Point(0, 5)\n", "x = pt1 + pt2" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(10, 20)\n" ] } ], "source": [ "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.5 단항(Unary) 산술 연산자" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", "\n", " def __add__(self, other):\n", " ''' self+other를 포함한 포인트 반환.'''\n", " newx = self.x + other.x\n", " newy = self.y + other.y\n", " return Point(newx, newy)\n", "\n", " def __sub__(self, other):\n", " ''' 두 포인트의 거리 반환.'''\n", " dx = self.x - other.x\n", " dy = self.y - other.y\n", " return (dx * dx + dy * dy) ** 0.5\n", "\n", " def __mul__(self, n):\n", " ''' point 곱하기 스칼라 숫자 n.'''\n", " newx = self.x * n\n", " newy = self.y * n\n", " return Point(newx, newy)\n", " \n", " def __neg__(self):\n", " newx = -self.x\n", " newy = -self.y\n", " return Point(newx, newy)\n", " \n", " __invert__ = __neg__\n", " \n", " def __trunc__(self):\n", " newx = self.x.__trunc__()\n", " newy = self.y.__trunc__()\n", " return Point(newx, newy)\n", "\n", " def __str__(self):\n", " return \"Point({0}, {1})\".format(self.x, self.y) " ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-3, -4, -4\n" ] } ], "source": [ "pt1 = Point(3, 4)\n", "pt2 = -pt1\n", "pt3 = ~pt1\n", "print(pt2.x, ', ', pt2.y, ', ', pt3.y, sep='')" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5, -6\n" ] } ], "source": [ "import math\n", "\n", "pt1 = Point(5.5, -6.6)\n", "pt2 = math.trunc(pt1)\n", "print(pt2.x, ', ', pt2.y, sep='')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.6 리플렉션(역방향) 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9\n" ] } ], "source": [ "# __add__ 구현이 없는 Dog 클래스 정의\n", "class Dog(): \n", " def __init__(self, num):\n", " self.num = num\n", "\n", "\n", "# __radd__ 구현이 있는 Cat 클래스 정의\n", "class Cat():\n", " def __init__(self, num):\n", " self.num = num\n", " \n", " def __radd__(self, other):\n", " return self.num + other.num\n", "\n", "fido = Dog(4)\n", "precious = Cat(5)\n", "print(fido + precious) # 우측 객체에만 __radd__ 가 존재한다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", "\n", " def __add__(self, other):\n", " ''' self+other를 포함한 포인트 반환.'''\n", " newx = self.x + other.x\n", " newy = self.y + other.y\n", " return Point(newx, newy)\n", "\n", " def __sub__(self, other):\n", " ''' 두 포인트의 거리 반환.'''\n", " dx = self.x - other.x\n", " dy = self.y - other.y\n", " return (dx * dx + dy * dy) ** 0.5\n", "\n", " def __mul__(self, n):\n", " ''' point 곱하기 스칼라 숫자 n.'''\n", " newx = self.x * n\n", " newy = self.y * n\n", " return Point(newx, newy)\n", " \n", " def __neg__(self):\n", " newx = -self.x\n", " newy = -self.y\n", " return Point(newx, newy)\n", " \n", " def __trunc__(self):\n", " newx = self.x.__trunc__()\n", " newy = self.y.__trunc__()\n", " return Point(newx, newy)\n", " \n", " def __rmul__(self, n):\n", " ''' 포인트와 스칼라 숫자 n과 곱셈한 결과를 반환한다 '''\n", " newx = self.x * n\n", " newy = self.y * n\n", " return Point(newx, newy)\n", "\n", " def __str__(self):\n", " return \"Point({0}, {1})\".format(self.x, self.y)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(6, 12)\n" ] } ], "source": [ "pt1 = Point(1, 2)\n", "pt2 = Point(5, 10)\n", "pt3 = pt1 + pt2\n", "print(pt3) # pt3 값 확인" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(5, 10)\n" ] } ], "source": [ "pt3 = pt1 * 5\n", "print(pt3) # pt3 값 확인" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(10, 20)\n" ] } ], "source": [ "pt3 = 10 * pt1\n", "print(pt3) # pt3 값 확인" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(-1, -2)\n" ] } ], "source": [ "pt4 = -pt1\n", "print(pt4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.7 교체(In-Place) 연산자 메서드 --> 인플레이스 연산자 라고 기억하자!!!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11 10\n" ] } ], "source": [ "# 역자주: 이해를 돕기 위해 MyClass 를 int 로 수정하였다\n", "a = int(10)\n", "b = a\n", "a += 1\n", "print(a, b) # a와 b가 여전히 같은 값을 가질까?" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", "\n", " def __iadd__(self, other):\n", " ''' self+other를 포함한 포인트 반환.'''\n", " newx = self.x + other.x\n", " newy = self.y + other.y\n", " return Point(newx, newy)\n", "\n", " def __isub__(self, other):\n", " ''' 두 포인트의 거리 반환.'''\n", " dx = self.x - other.x\n", " dy = self.y - other.y\n", " return (dx * dx + dy * dy) ** 0.5\n", "\n", " def __imul__(self, n):\n", " ''' point 곱하기 스칼라 숫자 n.'''\n", " newx = self.x * n\n", " newy = self.y * n\n", " return Point(newx, newy)\n", " \n", " def __str__(self):\n", " return \"Point({0}, {1})\".format(self.x, self.y)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(4, 6)\n" ] } ], "source": [ "pt1 = Point(1, 2)\n", "pt2 = Point(3, 4)\n", "pt1 += pt2\n", "print(pt1)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(15, 20)\n" ] } ], "source": [ "pt2 *= 5\n", "print(pt2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.8 변환 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x = 0, y = 0):\n", " self.x = x\n", " self.y = y\n", "\n", " def __int__(self):\n", " return int(self.x) + int(self.y)\n", "\n", " def __float__(self):\n", " return float(self.x) + float(self.y)\n", " \n", " def __str__(self):\n", " return \"Point({0}, {1})\".format(self.x, self.y)" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = Point(1, 2.5)\n", "int(p)" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "3.5" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.9 컬렉션 클래스 메서드" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Stack:\n", " def __init__(self):\n", " self.mylist = [] # Containment는 여기서 사용된다!\n", "\n", " def append(self, v):\n", " self.mylist.append(v)\n", "\n", " def push(self, v):\n", " self.mylist.append(v)\n", "\n", " def pop(self):\n", " return self.mylist.pop()\n", "\n", " def peek(self):\n", " return self.mylist[-1]\n", "\n", " def __len__(self):\n", " return len(self.mylist)\n", "\n", " def __contains__(self, v):\n", " return self.mylist.__contains__(v)\n", "\n", " def __getitem__(self, k):\n", " return self.mylist[k]" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Size of stack is: 4\n", "First elem is: 10\n", "True\n", "##########\n", "The top of the stack is: 40\n", "40\n", "30\n", "20\n", "Size of stack is: 1\n" ] } ], "source": [ "st = Stack()\n", "st.push(10)\n", "st.push(20)\n", "st.push(30)\n", "st.push(40)\n", "print('Size of stack is:', len(st))\n", "print('First elem is:', st[0])\n", "print(10 in st)\n", "print(\"#\" * 10)\n", "print('The top of the stack is:', st.peek())\n", "print(st.pop())\n", "print(st.pop())\n", "print(st.pop())\n", "print('Size of stack is:', len(st))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n" ] } ], "source": [ "print(st[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 9.10.10 \\_\\_iter\\_\\_와 \\_\\_next\\_\\_ 구현하기" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Seq:\n", " def __getitem__(self, n):\n", " if n == 10:\n", " raise IndexError()\n", " return n\n", " \n", "s = Seq() \n", "s[0]" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s[1]" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s[9]" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "ename": "IndexError", "evalue": "", "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[0;32m----> 1\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m]\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, n)\u001b[0m\n\u001b[1;32m 2\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[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mIndexError\u001b[0m: " ] } ], "source": [ "s[10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 이터레이터 (반복자) 구현\n", " - 내장 함수 iter()에 대응되는 \\_\\_iter\\_\\_(self) 및 next()에 대응되는 \\_\\_next\\_\\_(self)의 구현 \n", " - 객체 o에 iter()를 호출하면 자동으로 \\_\\_iter\\_\\_(self) 함수 호출\n", " - \\_\\_iter\\_\\_(self) 함수는 \\_\\_next\\_\\_(self) 함수를 지닌 반복자 객체를 반환" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- for ~ in 호출시에는 \\_\\_next\\_\\_() 함수가 \\_\\_getitem\\_\\_() 보다 우선하여 호출됨" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc\n", "\n", "def\n", "\n", "ghi\n", "\n", "<__main__.Seq object at 0x7f831826c438>\n", "['abc\\n', 'def\\n', 'ghi']\n", "('abc\\n', 'def\\n', 'ghi')\n" ] } ], "source": [ "class Seq:\n", " def __init__(self, file):\n", " self.file = open(file)\n", "\n", " def __iter__(self):\n", " return self\n", " \n", " def __next__(self):\n", " line = self.file.readline() # 한 라인을 읽는다.\n", " if not line: \n", " raise StopIteration # 읽을 수 없으면 예외 발생\n", " return line # 읽은 라인을 리턴한다.\n", " \n", "s = Seq('readme.txt') # s 인스턴스가 next() 메소드를 지니고 있으므로 s 인스턴스 자체가 반복자임 \n", "\n", "for line in s: # 우선 __iter__() 메소드를 호출하여 반복자를 얻고, 반복자에 대해서 for ~ in 구문에 의하여 __next__() 메소드가 호출됨\n", " print(line)\n", "\n", "print()\n", "\n", "print(Seq('readme.txt'))\n", "\n", "# list() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. \n", "print(list(Seq('readme.txt'))) \n", "\n", "# tuple() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. \n", "print(tuple(Seq('readme.txt'))) " ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Stack:\n", " def __init__(self):\n", " self.mylist = [] # Containment는 여기서 사용된다!\n", "\n", " def append(self, v):\n", " self.mylist.append(v)\n", "\n", " def push(self, v):\n", " self.mylist.append(v)\n", "\n", " def pop(self):\n", " return self.mylist.pop()\n", "\n", " def peek(self):\n", " return self.mylist[-1]\n", "\n", " def __len__(self):\n", " return len(self.mylist)\n", "\n", " def __contains__(self, v):\n", " return self.mylist.__contains__(v)\n", "\n", " def __getitem__(self, k):\n", " return self.mylist[k]\n", " \n", " def __iter__(self):\n", " self.current = 0\n", " return self\n", "\n", " def __next__(self):\n", " if self.current < len(self):\n", " self.current += 1\n", " return self.mylist[self.current - 1]\n", " else:\n", " raise StopIteration\n" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "20\n", "30\n", "40\n" ] } ], "source": [ "# 역자주: 테스트 코드\n", "st = Stack()\n", "st.push(10)\n", "st.push(20)\n", "st.push(30)\n", "st.push(40)\n", "\n", "for s in st:\n", " print(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.11 다중 인수 타입 지원" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "n is integer.\n" ] } ], "source": [ "n = 5\n", "if type(n) == int:\n", " print('n is integer.')" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "n is an integer or derived from it.\n" ] } ], "source": [ "n = 5\n", "if isinstance(n, int):\n", " print('n is an integer or derived from it.')" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "n is numeric.\n" ] } ], "source": [ "if isinstance(n, (int, float)):\n", " print('n is numeric.')" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", " \n", " def __mul__(self, other):\n", " if type(other) == Point:\n", " newx = self.x * other.x\n", " newy = self.y * other.y\n", " return Point(newx, newy)\n", " elif type(other) == int or type(other) == float:\n", " newx = self.x * other\n", " newy = self.y * other\n", " return Point(newx, newy)\n", " else:\n", " return NotImplemented" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Point:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", " \n", " def __mul__(self, other):\n", " if isinstance(other, Point):\n", " newx = self.x * other.x\n", " newy = self.y * other.y\n", " return Point(newx, newy)\n", " elif isinstance(other, (int, float)):\n", " newx = self.x * other\n", " newy = self.y * other\n", " return Point(newx, newy)\n", " else:\n", " return NotImplemented\n", " \n", " def __rmul__(self, other):\n", " if isinstance(other, (int, float)):\n", " newx = self.x * other\n", " newy = self.y * other\n", " return Point(newx, newy)\n", " else:\n", " return NotImplemented" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "pt1 = Point(2, 3)\n", "pt2 = 5.5 * pt1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.12 동적 속성 설정(Setting) 및 조회(Getting)" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Great Dane\n" ] } ], "source": [ "class Dog:\n", " pass\n", "\n", "d = Dog()\n", "\n", "setattr(d, 'breed', 'Great Dane')\n", "\n", "print(d.breed)" ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "'Great Dane'" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "field = 'breed'\n", "getattr(d, field)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [참고 자료] - 클래스 내 연산자 중복 (Operator Overloading) 정의\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 수치 연산자 중복\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", "\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

__truediv__(self, B)

/

o / B, o /= B

__rtruediv__(self, B)

/

B / o

__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": 149, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "10\n", "\n", "20\n", "\n", "30\n", "\n", "45\n", "\n", "450\n" ] } ], "source": [ "class MyInteger:\n", " def __init__(self, i):\n", " self.i = i\n", "\n", " def __str__(self):\n", " return str(self.i)\n", "\n", " def __add__(self, other):\n", " return self.i + other\n", "\n", " def __sub__(self, other):\n", " return self.i - other\n", "\n", " def __mul__(self, other):\n", " return self.i * other\n", "\n", "\n", "i = MyInteger(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)\n", "\n", "print()\n", "i += 15\n", "print(i)\n", "\n", "print()\n", "i *= 10\n", "print(i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- [주의] 위 코드는 연산자 오버로딩을 올바로 설명하지 못하는 코드. 아래 코드가 올바른 코드임. 이유는?" ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "10\n", "\n", "20\n", "\n", "30\n", "\n", "45\n", "\n", "450\n" ] } ], "source": [ "class MyInteger2:\n", " def __init__(self, i):\n", " self.i = i\n", "\n", " def __str__(self):\n", " return str(self.i)\n", "\n", " def __add__(self, other):\n", " return MyInteger2(self.i + other)\n", "\n", " def __sub__(self, other):\n", " return MyInteger2(self.i - other)\n", "\n", " def __mul__(self, other):\n", " return MyInteger2(self.i * other)\n", "\n", "\n", "i = MyInteger2(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)\n", "\n", "print()\n", "i += 15\n", "print(i)\n", "\n", "print()\n", "i *= 10\n", "print(i)" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", "\n", " def __truediv__(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.__truediv__(\"_\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 연산자 왼쪽에 피연산자, 연산자 오른쪽에 객체가 오는 경우\n", " - 메소드 이름 앞에 r이 추가된 메소드 정의" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", "\n", " def __truediv__(self, sep):\n", " return str.split(self.str, sep)\n", "\n", " __rtruediv__ = __truediv__\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": 153, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fedcba\n", "fedcba\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", "\n", " def __neg__(self):\n", " t = list(self.str)\n", " t.reverse()\n", " return ''.join(t)\n", "\n", " __invert__ = __neg__\n", "\n", "m = MyString(\"abcdef\")\n", "print(-m)\n", "print(~m)" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fedcba\n", "abcdef\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", "\n", " def __neg__(self):\n", " t = list(self.str)\n", " t.reverse()\n", " return MyString(''.join(t))\n", "\n", " def __str__(self):\n", " return self.str\n", "\n", " __invert__ = __neg__\n", "\n", "m = MyString(\"abcdef\")\n", "\n", "m = -m\n", "print(m)\n", "\n", "m = ~m\n", "print(m)" ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "aaaa\n", "bbbb\n", "('aaaa', 'cccc')\n", "\n", "aaaa\n", "bbbb\n", "('aaaa', 'cccc')\n" ] } ], "source": [ "class MyString:\n", " def __init__(self, str):\n", " self.str = str\n", "\n", " def __floordiv__(self, sep):\n", " return self.str.split(sep)[0]\n", "\n", " def __mod__(self, sep):\n", " return self.str.split(sep)[1]\n", "\n", " def __divmod__(self, sep):\n", " seperated_list = self.str.split(sep)\n", " return seperated_list[0], seperated_list[-1]\n", "\n", "m = MyString(\"aaaa_bbbb_cccc\")\n", "print(m // \"_\")\n", "print(m % \"_\")\n", "print(divmod(m, \"_\"))\n", "\n", "print()\n", "print(m.__floordiv__(\"_\"))\n", "print(m.__mod__(\"_\"))\n", "print(m.__divmod__(\"_\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 비교 연산자 중복" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 각각의 비교 연산에 대응되는 메소드 이름이 정해져 있지만 그러한 메소드가 별도로 정의되어 있지 않으면 cmp가 호출됨\n", "\n", "|메소드 |연산자 | 비고 |\n", "|--------|----|----|\n", "|\\_\\_cmp\\_\\_(self, other) | 아래 메소드가 부재한 상황에 호출되는 메소드| python3.x 에서는 지원하지 않음 |\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": "code", "execution_count": 156, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "False\n", "False\n", "\n", "False\n", "True\n" ] } ], "source": [ "class MyInteger:\n", " def __init__(self, i):\n", " self.i = i\n", "\n", " def __gt__(self, y):\n", " return self.i > y\n", "\n", " def __ge__(self, y):\n", " return self.i >= y\n", "\n", " def __lt__(self, y):\n", " return self.i < y\n", "\n", " def __le__(self, y):\n", " return self.i <= y\n", "\n", " def __eq__(self, y):\n", " return self.i == y\n", "\n", " def __ne__(self, y):\n", " return self.i != y\n", "\n", "c = MyInteger(10)\n", "print(c > 1)\n", "print(c >= 1)\n", "print(c < 1)\n", "print(c <= 1)\n", "print()\n", "print(c == 1)\n", "print(c != 1)" ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "False\n", "\n", "False\n", "False\n", "\n", "True\n", "True\n", "\n", "True\n", "True\n", "\n", "False\n", "True\n", "\n", "False\n", "True\n" ] } ], "source": [ "class MyInteger:\n", " def __init__(self, i):\n", " self.i = i\n", "\n", " def __gt__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i > y.i\n", " elif isinstance(y, int):\n", " return self.i > y\n", " else:\n", " return False\n", "\n", " def __ge__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i >= y.i\n", " elif isinstance(y, int):\n", " return self.i >= y\n", " else:\n", " return False\n", "\n", " def __lt__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i < y.i\n", " elif isinstance(y, int):\n", " return self.i < y\n", " else:\n", " return False\n", "\n", " def __le__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i <= y.i\n", " elif isinstance(y, int):\n", " return self.i <= y\n", " else:\n", " return False\n", "\n", " def __eq__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i == y.i\n", " elif isinstance(y, int):\n", " return self.i == y\n", " else:\n", " return False\n", "\n", " def __ne__(self, y):\n", " if isinstance(y, MyInteger):\n", " return self.i != y.i\n", " elif isinstance(y, int):\n", " return self.i != y\n", " else:\n", " return False\n", "\n", "c = MyInteger(10)\n", "d = MyInteger(100)\n", "e = 50\n", "\n", "print(c > d)\n", "print(c >= d)\n", "print()\n", "\n", "print(c > e)\n", "print(c >= e)\n", "print()\n", "\n", "print(c < d)\n", "print(c <= d)\n", "print()\n", "\n", "print(c < e)\n", "print(c <= e)\n", "print()\n", "\n", "print(c == d)\n", "print(c != d)\n", "print()\n", "\n", "print(c == e)\n", "print(c != e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***\n", "### [시퀀스/매핑 자료형의 연산자 중복]\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 클래스를 개발할 때 다음 메소드들을 적절하게 구현하면 자신만의 시퀀스 자료형을 만들 수 있음\n", "- 변경불가능한 (Immutable) 시퀀스 자료형 및 매핑 자료형을 위해 구현이 필요한 메소드" ] }, { "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": [ "#### 인덱싱 (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": 158, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "\n", "0\n", "1\n", "16\n", "81\n" ] }, { "ename": "IndexError", "evalue": "Out of Index - 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 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\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[0;31m#s1.__getitem__(4)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m9\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#s1.__getitem__(9)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\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[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 8\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\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[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIndexError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Out of Index - \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\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[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mIndexError\u001b[0m: Out of Index - 20" ] } ], "source": [ "class Square:\n", " def __init__(self, end):\n", " self.end = end\n", "\n", " def __len__(self):\n", " return self.end\n", "\n", " def __getitem__(self, k):\n", " if k < 0 or self.end <= k:\n", " raise IndexError(\"Out of Index - \" + str(k))\n", " return k * k\n", "\n", "s1 = Square(10)\n", "print(len(s1)) # s1.__len__()\n", "print()\n", "print(s1[0]) #s1.__getitem__(0)\n", "print(s1[1]) #s1.__getitem__(1)\n", "print(s1[4]) #s1.__getitem__(4)\n", "print(s1[9]) #s1.__getitem__(9)\n", "print(s1[20])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 다음 for 문은 s1에 대해 \\_\\_getitem()\\_\\_ 메소드를 0부터 호출하여 IndexError가 발생하면 루프를 중단한다." ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1 4 9 16 25 36 49 64 81 " ] } ], "source": [ "s1 = Square(10)\n", "for x in s1:\n", " print(x, end=\" \")" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1 4 9 16 25 36 49 64 81 " ] } ], "source": [ "s2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n", "for x in s2:\n", " #print(x,)\n", " print(x, end=\" \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_getitem\\_\\_() 메소드가 정의되어 있다면 다른 시퀀스 자료형으로 변환이 가능" ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "pycharm": { "name": "#%%\n" } }, "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": [ "#### 매핑 자료형의 연산자 중복" ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.MyDict object at 0x7fc818167828>\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", "\n", " def __getitem__(self, k): #key\n", " return self.d[k]\n", "\n", " def __setitem__(self, k, v):\n", " self.d[k] = v\n", "\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": 163, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['one', 'two', 'three']\n", "[1, 2, 3]\n", "[('one', 1), ('two', 2), ('three', 3)]\n" ] } ], "source": [ "class MyDict:\n", " def __init__(self, d=None):\n", " if d == None: d = {}\n", " self.d = d\n", "\n", " def __getitem__(self, k):\n", " return self.d[k]\n", "\n", " def __setitem__(self, k, v):\n", " self.d[k] = v\n", "\n", " def __len__(self):\n", " return len(self.d)\n", "\n", " def keys(self):\n", " return list(self.d.keys())\n", "\n", " def values(self):\n", " return list(self.d.values())\n", "\n", " def items(self):\n", " return list(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", "### [문자열 변환과 호출 가능 객체]\n", "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 문자열로 변환하기\n", "1) \\_\\_repr\\_\\_\n", "- 객체를 대표하여 유일하게 표현할 수 있는 공식적인 문자열\n", "- eval() 함수에 의하여 같은 객체로 재생성 될 수 있는 문자열 표현\n", "\n", "2) \\_\\_str\\_\\_\n", "- 객체의 비공식적인 문자열 표현\n", "- 사용자가 보기 편한 형태로 자유롭게 표현될 수 있음" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str called\n", "str called\n", "repr called\n" ] } ], "source": [ "class StringRepr:\n", " def __repr__(self):\n", " return 'repr called'\n", "\n", " def __str__(self):\n", " return 'str called'\n", "\n", "s = StringRepr()\n", "print(s)\n", "print(str(s))\n", "print(repr(s))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_str\\_\\_() 호출시\n", " - \\_\\_str\\_\\_()가 정의되어 있지 않으면 \\_\\_repr\\_\\_()이 대신 호출됨" ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "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(str(s))\n", "print(repr(s))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- \\_\\_repr\\_\\_() 호출시\n", " - \\_\\_repr\\_\\_()이 정의되어 있지 않으면 객체 식별자가 출력됨\n", " - 대신하여 \\_\\_str\\_\\_()이 호출되지 않음" ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str called\n", "str called\n", "<__main__.StringRepr object at 0x7fc818173a58>\n" ] } ], "source": [ "class StringRepr:\n", " def __str__(self):\n", " return 'str called'\n", "\n", "s = StringRepr()\n", "print(s)\n", "print(str(s))\n", "print(repr(s))" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eval('10 + 20')" ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 168, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = '10 + 20'\n", "eval(a)" ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc\n" ] } ], "source": [ "b = \"print('abc')\"\n", "eval(b)" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100\n" ] } ], "source": [ "class StringRepr:\n", " def __init__(self, i = 10):\n", " self.i = i\n", "\n", " def __repr__(self):\n", " return 'StringRepr(100)'\n", "\n", "s = StringRepr()\n", "q = eval(repr(s))\n", "print(q.i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 호출 가능한 클래스 인스턴스 만들기\n", "- 클래스 인스턴스에 \\_\\_call\\_\\_ 메소드가 구현되어 있다면 해당 인스턴스는 함수와 같이 호출될 수 있다.\n", " - 인스턴스 x에 대해 x(a1, a2, a3)와 같이 호출된다면 x.\\_\\_call\\_\\_(a1, a2, a3)가 호출된다." ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "pycharm": { "name": "#%%\n" } }, "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", "\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": "markdown", "metadata": {}, "source": [ "- 호출 가능 객체인지 알아보기" ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "callable\n", "not callable\n", "\n", "True\n", "False\n" ] } ], "source": [ "class A:\n", " def __call__(self, v):\n", " return v\n", "\n", "class B:\n", " def func(self, v):\n", " return v\n", "\n", "def check(func):\n", " if callable(func):\n", " print('callable')\n", " else:\n", " print('not callable')\n", "\n", "a = A()\n", "b = B()\n", "\n", "check(a)\n", "check(b)\n", "print()\n", "print(callable(a))\n", "print(callable(b))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.13 정리해보자\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.14 복습 문제" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9.15 실습 문제" ] } ], "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" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }