|
28 | 28 | "클래스와 객체를 보다 실용적으로 활용하는 두 가지 방법 **상속**과 **구성**을 소개하면서\n", |
29 | 29 | "객체 지향 프로그래밍(OOP)의 기본 디자인 원칙을 살펴본다.\n", |
30 | 30 | "\n", |
31 | | - "이번 장에서는 먼저 `Person` 클래스를 정의하고, 사람의 기본 속성을 물려받는 `Hero` 클래스를 만드는 방식으로\n", |
| 31 | + "이번 장에서는 먼저 `Human` 클래스를 정의하고, 사람의 기본 속성을 물려받는 `Hero` 클래스를 만드는 방식으로\n", |
32 | 32 | "상속의 필요성과 장단점을 살펴본다." |
33 | 33 | ] |
34 | 34 | }, |
|
48 | 48 | "객체 지향 프로그래밍에서는 필요한 자료형을 클래스로 정의하고,\n", |
49 | 49 | "그 클래스로부터 여러 개의 인스턴스를 생성하여 사용할 수 있다.\n", |
50 | 50 | "\n", |
51 | | - "먼저 사람을 나타내는 가장 단순한 `Person` 클래스를 정의하자.\n", |
| 51 | + "먼저 사람을 나타내는 가장 단순한 `Human` 클래스를 정의하자.\n", |
52 | 52 | "여기서는 설명을 단순하게 하기 위해 사람의 속성을 이름, 나이, 성별로 제한한다." |
53 | 53 | ] |
54 | 54 | }, |
|
67 | 67 | "metadata": {}, |
68 | 68 | "outputs": [], |
69 | 69 | "source": [ |
70 | | - "class Person:\n", |
| 70 | + "class Human:\n", |
71 | 71 | " def __init__(self, name, age, gender):\n", |
72 | 72 | " self.name = name\n", |
73 | 73 | " self.age = age\n", |
|
94 | 94 | "metadata": {}, |
95 | 95 | "outputs": [], |
96 | 96 | "source": [ |
97 | | - "kim = Person(\"김민수\", 20, \"남성\")\n", |
98 | | - "lee = Person(\"이영희\", 21, \"여성\")" |
| 97 | + "kim = Human(\"김민수\", 20, \"남성\")\n", |
| 98 | + "lee = Human(\"이영희\", 21, \"여성\")" |
99 | 99 | ] |
100 | 100 | }, |
101 | 101 | { |
|
149 | 149 | "id": "a77b4e64", |
150 | 150 | "metadata": {}, |
151 | 151 | "source": [ |
152 | | - "가장 단순한 방법은 `Person` 클래스와 비슷한 코드를 다시 작성하여 `Hero` 클래스를 새로 만드는 것이다.\n", |
| 152 | + "가장 단순한 방법은 `Human` 클래스와 비슷한 코드를 다시 작성하여 `Hero` 클래스를 새로 만드는 것이다.\n", |
153 | 153 | "하지만 이렇게 하면 이름, 나이, 성별을 저장하고 자기소개를 하는 코드가 중복된다." |
154 | 154 | ] |
155 | 155 | }, |
|
222 | 222 | "metadata": {}, |
223 | 223 | "source": [ |
224 | 224 | "하지만 이 방식은 좋은 설계가 아니다.\n", |
225 | | - "`Person`과 `Hero`가 공유하는 코드가 중복되며, 사람의 기본 정보 처리 방식이 바뀌면\n", |
| 225 | + "`Human`과 `Hero`가 공유하는 코드가 중복되며, 사람의 기본 정보 처리 방식이 바뀌면\n", |
226 | 226 | "두 클래스를 모두 수정해야 한다.\n", |
227 | 227 | "\n", |
228 | 228 | "이런 문제를 해결하기 위해 **상속**<font size='2'>inheritance</font>을 사용할 수 있다." |
|
243 | 243 | "source": [ |
244 | 244 | "**예제 1**\n", |
245 | 245 | "\n", |
246 | | - "`Person` 클래스와 상속을 사용하지 않은 `Hero` 클래스에서 중복되는 코드를 찾아 설명하라." |
| 246 | + "`Human` 클래스와 상속을 사용하지 않은 `Hero` 클래스에서 중복되는 코드를 찾아 설명하라." |
247 | 247 | ] |
248 | 248 | }, |
249 | 249 | { |
|
265 | 265 | "source": [ |
266 | 266 | "**예제 2**\n", |
267 | 267 | "\n", |
268 | | - "아래 코드처럼 `Person.introduction()`의 출력 형식을 바꾸고 싶다고 하자.\n", |
| 268 | + "아래 코드처럼 `Human.introduction()`의 출력 형식을 바꾸고 싶다고 하자.\n", |
269 | 269 | "\n", |
270 | 270 | "```python\n", |
271 | 271 | "def introduction(self):\n", |
|
282 | 282 | "source": [ |
283 | 283 | "답:\n", |
284 | 284 | "\n", |
285 | | - "`Person` 클래스만 수정하면 `Hero` 객체의 자기소개 형식은 바뀌지 않는다.\n", |
| 285 | + "`Human` 클래스만 수정하면 `Hero` 객체의 자기소개 형식은 바뀌지 않는다.\n", |
286 | 286 | "두 클래스가 같은 기능을 각각 구현했기 때문에 `Hero.introduction()`도 별도로 수정해야 한다.\n", |
287 | 287 | "클래스가 많아질수록 빠뜨리는 클래스가 생기기 쉽고, 같은 기능의 동작이 서로 달라질 위험도 커진다." |
288 | 288 | ] |
|
307 | 307 | } |
308 | 308 | ], |
309 | 309 | "source": [ |
310 | | - "kim = Person(\"김민수\", 20, \"남성\")\n", |
| 310 | + "kim = Human(\"김민수\", 20, \"남성\")\n", |
311 | 311 | "ironman = Hero(\n", |
312 | 312 | " \"아이언맨\", 45, \"남성\",\n", |
313 | 313 | " 100, 100, 200,\n", |
|
429 | 429 | "“영웅은 사람이다”처럼 자식 클래스의 객체를\n", |
430 | 430 | "부모 클래스의 객체로 보아도 자연스러운지 신중하게 판단해야 한다.\n", |
431 | 431 | "\n", |
432 | | - "`Hero`는 특별한 능력을 가진 사람이므로 `Person`의 한 종류로 볼 수 있다.\n", |
433 | | - "따라서 `Person`을 상속하는 자식 클래스로 `Hero`를 선언할 수 있다.\n", |
434 | | - "`Hero`는 `Person`의 메서드를 물려받고, 부모 클래스의 `__init__()`을 호출하여\n", |
| 432 | + "`Hero`는 특별한 능력을 가진 사람이므로 `Human`의 한 종류로 볼 수 있다.\n", |
| 433 | + "따라서 `Human`을 상속하는 자식 클래스로 `Hero`를 선언할 수 있다.\n", |
| 434 | + "`Hero`는 `Human`의 메서드를 물려받고, 부모 클래스의 `__init__()`을 호출하여\n", |
435 | 435 | "사람의 기본 속성을 초기화한 뒤 영웅에게 필요한 속성과 메서드를 추가한다." |
436 | 436 | ] |
437 | 437 | }, |
|
442 | 442 | "metadata": {}, |
443 | 443 | "outputs": [], |
444 | 444 | "source": [ |
445 | | - "class Hero(Person):\n", |
| 445 | + "class Hero(Human):\n", |
446 | 446 | " def __init__(self, name, age, gender, power, health, damage, inventory):\n", |
447 | 447 | " super().__init__(name, age, gender)\n", |
448 | 448 | " self.power = power\n", |
|
461 | 461 | "id": "a8133a2f", |
462 | 462 | "metadata": {}, |
463 | 463 | "source": [ |
464 | | - "`Hero` 클래스는 `Person` 클래스를 상속한다.\n", |
| 464 | + "`Hero` 클래스는 `Human` 클래스를 상속한다.\n", |
465 | 465 | "따라서 `Hero`의 인스턴스는 영웅 객체이면서 동시에 사람 객체이기도 하다." |
466 | 466 | ] |
467 | 467 | }, |
|
485 | 485 | "source": [ |
486 | 486 | "ironman = Hero(\"아이언맨\", 45, \"남성\", 100, 100, 200, {\"suit\": 500, \"weapon\": \"레이저\"})\n", |
487 | 487 | "\n", |
488 | | - "isinstance(ironman, Hero), isinstance(ironman, Person)" |
| 488 | + "isinstance(ironman, Hero), isinstance(ironman, Human)" |
489 | 489 | ] |
490 | 490 | }, |
491 | 491 | { |
492 | 492 | "cell_type": "markdown", |
493 | 493 | "id": "f9e02f21", |
494 | 494 | "metadata": {}, |
495 | 495 | "source": [ |
496 | | - "`Hero`는 `Person`의 메서드를 물려받으므로 `introduction()` 메서드를 바로 사용할 수 있다." |
| 496 | + "`Hero`는 `Human`의 메서드를 물려받으므로 `introduction()` 메서드를 바로 사용할 수 있다." |
497 | 497 | ] |
498 | 498 | }, |
499 | 499 | { |
|
540 | 540 | } |
541 | 541 | ], |
542 | 542 | "source": [ |
543 | | - "park = Person(\"박지민\", 19, \"여성\")\n", |
| 543 | + "park = Human(\"박지민\", 19, \"여성\")\n", |
544 | 544 | "\n", |
545 | 545 | "try:\n", |
546 | 546 | " park.attack(ironman)\n", |
|
552 | 552 | "cell_type": "markdown", |
553 | 553 | "metadata": {}, |
554 | 554 | "source": [ |
555 | | - "`park`은 `Person` 클래스의 객체이기는 하지만 `Hero` 클래스의 객체는 아니다." |
| 555 | + "`park`은 `Human` 클래스의 객체이기는 하지만 `Hero` 클래스의 객체는 아니다." |
556 | 556 | ] |
557 | 557 | }, |
558 | 558 | { |
|
572 | 572 | } |
573 | 573 | ], |
574 | 574 | "source": [ |
575 | | - "isinstance(park, Person), isinstance(park, Hero)" |
| 575 | + "isinstance(park, Human), isinstance(park, Hero)" |
576 | 576 | ] |
577 | 577 | }, |
578 | 578 | { |
|
591 | 591 | "**`__init__()` 메서드 재정의와 `super()`**\n", |
592 | 592 | "\n", |
593 | 593 | "`Hero` 클래스는 상속받은 `__init__()` 메서드를 재정의한다.\n", |
594 | | - "재정의된 메서드에서는 `super()`를 사용하여 부모 클래스인 `Person`의\n", |
| 594 | + "재정의된 메서드에서는 `super()`를 사용하여 부모 클래스인 `Human`의\n", |
595 | 595 | "`__init__()` 메서드를 호출한다.\n", |
596 | 596 | "\n", |
597 | 597 | "```python\n", |
598 | 598 | "super().__init__(name, age, gender)\n", |
599 | 599 | "```\n", |
600 | 600 | "\n", |
601 | | - "이를 통해 `Person`이 담당하는 `name`, `age`, `gender` 속성을\n", |
| 601 | + "이를 통해 `Human`이 담당하는 `name`, `age`, `gender` 속성을\n", |
602 | 602 | "`Hero` 인스턴스에 초기화할 수 있다.\n", |
603 | 603 | "이때 현재 인스턴스가 자동으로 전달되므로 `self`를 직접 전달하지 않는다.\n", |
604 | 604 | "\n", |
|
610 | 610 | "부모 클래스에서 상속받은 메서드를 자식 클래스에서 같은 이름으로 다시 정의하는 것을\n", |
611 | 611 | "**메서드 오버라이딩**<font size='2'>method overriding</font>이라고 한다.\n", |
612 | 612 | "\n", |
613 | | - "아래 `Hero` 클래스는 부모 클래스인 `Person` 클래스로부터 상속받은 `introduction()` 메서드도 재정의하여\n", |
| 613 | + "아래 `Hero` 클래스는 부모 클래스인 `Human` 클래스로부터 상속받은 `introduction()` 메서드도 재정의하여\n", |
614 | 614 | "일반적인 자기소개와 함께 파워와 무기 정보도 제공한다." |
615 | 615 | ] |
616 | 616 | }, |
|
621 | 621 | "metadata": {}, |
622 | 622 | "outputs": [], |
623 | 623 | "source": [ |
624 | | - "class Hero(Person):\n", |
| 624 | + "class Hero(Human):\n", |
625 | 625 | " def __init__(self, name, age, gender, power, health, damage, inventory):\n", |
626 | 626 | " super().__init__(name, age, gender)\n", |
627 | 627 | " self.power = power\n", |
|
684 | 684 | "source": [ |
685 | 685 | "**예제 1**\n", |
686 | 686 | "\n", |
687 | | - "아래 코드의 실행 결과를 예측하고, `Hero` 객체가 `Person`의 인스턴스로도 판정되는 이유를 설명하라.\n", |
| 687 | + "아래 코드의 실행 결과를 예측하고, `Hero` 객체가 `Human`의 인스턴스로도 판정되는 이유를 설명하라.\n", |
688 | 688 | "\n", |
689 | 689 | "```python\n", |
690 | 690 | "print(isinstance(ironman, Hero))\n", |
691 | | - "print(isinstance(ironman, Person))\n", |
| 691 | + "print(isinstance(ironman, Human))\n", |
692 | 692 | "print(isinstance(park, Hero))\n", |
693 | 693 | "```" |
694 | 694 | ] |
|
701 | 701 | "답:\n", |
702 | 702 | "\n", |
703 | 703 | "차례대로 `True`, `True`, `False`가 출력된다.\n", |
704 | | - "`Hero`는 `Person`을 상속하므로 `Hero`의 인스턴스는 자식 클래스인 `Hero`의 인스턴스이면서\n", |
705 | | - "부모 클래스인 `Person`의 인스턴스이기도 하다.\n", |
706 | | - "반대로 `Person`의 인스턴스가 자동으로 `Hero`의 인스턴스가 되지는 않는다." |
| 704 | + "`Hero`는 `Human`을 상속하므로 `Hero`의 인스턴스는 자식 클래스인 `Hero`의 인스턴스이면서\n", |
| 705 | + "부모 클래스인 `Human`의 인스턴스이기도 하다.\n", |
| 706 | + "반대로 `Human`의 인스턴스가 자동으로 `Hero`의 인스턴스가 되지는 않는다." |
707 | 707 | ] |
708 | 708 | }, |
709 | 709 | { |
|
724 | 724 | ], |
725 | 725 | "source": [ |
726 | 726 | "print(isinstance(ironman, Hero))\n", |
727 | | - "print(isinstance(ironman, Person))\n", |
| 727 | + "print(isinstance(ironman, Human))\n", |
728 | 728 | "print(isinstance(park, Hero))" |
729 | 729 | ] |
730 | 730 | }, |
|
769 | 769 | } |
770 | 770 | ], |
771 | 771 | "source": [ |
772 | | - "class IncompleteHero(Person):\n", |
| 772 | + "class IncompleteHero(Human):\n", |
773 | 773 | " def __init__(self, name, age, gender, power):\n", |
774 | 774 | " self.power = power\n", |
775 | 775 | "\n", |
|
826 | 826 | "source": [ |
827 | 827 | "**문제 1**\n", |
828 | 828 | "\n", |
829 | | - "`Person`을 상속하는 `Student` 클래스를 정의하라.\n", |
| 829 | + "`Human`을 상속하는 `Student` 클래스를 정의하라.\n", |
830 | 830 | "`Student` 클래스는 `school` 속성을 추가로 가지며,\n", |
831 | 831 | "생성자에서 `super()`를 사용하여 이름, 나이, 성별을 초기화해야 한다." |
832 | 832 | ] |
|
849 | 849 | "source": [ |
850 | 850 | "**문제 3**\n", |
851 | 851 | "\n", |
852 | | - "다음 객체를 생성한 후 `isinstance()`를 이용하여 `student`가 `Student`와 `Person`의 인스턴스인지 각각 확인하라.\n", |
| 852 | + "다음 객체를 생성한 후 `isinstance()`를 이용하여 `student`가 `Student`와 `Human`의 인스턴스인지 각각 확인하라.\n", |
853 | 853 | "\n", |
854 | 854 | "```python\n", |
855 | 855 | "student = Student(\"김하나\", 18, \"여성\", \"파이썬고\")\n", |
|
1335 | 1335 | "metadata": {}, |
1336 | 1336 | "outputs": [], |
1337 | 1337 | "source": [ |
1338 | | - "class Hero(Person):\n", |
| 1338 | + "class Hero(Human):\n", |
1339 | 1339 | " def __init__(self, name, age, gender, power, health, damage, inventory, flying=None):\n", |
1340 | 1340 | " super().__init__(name, age, gender)\n", |
1341 | 1341 | " self.power = power\n", |
|
1636 | 1636 | "**디자인 원칙 2: 상속과 구성을 함께 활용하라.**\n", |
1637 | 1637 | "\n", |
1638 | 1638 | "상속과 구성은 서로 대체하는 기법이라기보다 서로 다른 관계를 표현하는 도구이다.\n", |
1639 | | - "`Hero`는 `Person`의 한 종류이므로 두 클래스는 **~이다**<font size='2'>is-a</font> 관계에 있고,\n", |
| 1639 | + "`Hero`는 `Human`의 한 종류이므로 두 클래스는 **~이다**<font size='2'>is-a</font> 관계에 있고,\n", |
1640 | 1640 | "사람의 공통 속성과 기능을 상속하는 것이 자연스럽다.\n", |
1641 | 1641 | "반면 영웅은 비행 방식의 한 종류가 아니라 비행 기능을 **가지므로**\n", |
1642 | 1642 | "`Hero`와 `Flying`은 **~을 가진다**<font size='2'>has-a</font> 관계로 표현하는 편이 자연스럽다.\n", |
|
1745 | 1745 | "source": [ |
1746 | 1746 | "**문제 1**\n", |
1747 | 1747 | "\n", |
1748 | | - "현재 예제에서 `Person`, `Hero`, `Flying` 클래스가 각각 담당하는 책임을 한 문장씩 정리하라." |
| 1748 | + "현재 예제에서 `Human`, `Hero`, `Flying` 클래스가 각각 담당하는 책임을 한 문장씩 정리하라." |
1749 | 1749 | ] |
1750 | 1750 | }, |
1751 | 1751 | { |
|
0 commit comments