├── Algorithm ├── Sorting │ ├── insertion_sort.py │ ├── bubble_sort.py │ ├── count_sort.py │ ├── selection_sort.py │ ├── quick_sort.py │ └── merge_sort.py └── binary_search.py ├── Data_structure ├── queue.py ├── stack.py ├── stack_with_node.py ├── linked_list.py └── binary_tree.py ├── Linux └── Package manager.md ├── CS ├── Variable.md ├── Character and Strings.md ├── Integer.md ├── Real number.md ├── Class.md ├── Function.md └── Object-oriented programming.md ├── HTML-CSS ├── HTML_intermediates.md └── HTML_basics.md ├── Python ├── What is first-class function.md ├── How to handle errors.md ├── How to use abstract class.md ├── How to use class method and static method.md ├── How to use iterator and generator.md ├── How to implement information hiding.md ├── How to use argparse module.md ├── How to use decorator.md ├── How to use functional programming.md └── How to use closure.md ├── .gitignore ├── Git ├── Git_github.md └── Git_basics.md ├── README.md ├── Clean_code └── CC01_Introduction, Tools and Formatting.md └── Tensorflow ├── How to implement reusing variables.ipynb ├── How to simply use tf.data.ipynb └── How to use TFRecord format.ipynb /Algorithm/Sorting/insertion_sort.py: -------------------------------------------------------------------------------- 1 | def insertion_sort(seq): 2 | for i in range(1, len(seq)): 3 | j = i 4 | while j > 0 and seq[j - 1] > seq[j]: 5 | seq[j - 1], seq[j] = seq[j], seq[j - 1] 6 | j -= 1 7 | return seq 8 | 9 | 10 | def test_insertion_sort(): 11 | seq = [11, 3, 28, 43, 9, 4] 12 | assert insertion_sort(seq) == sorted(seq) 13 | 14 | 15 | if __name__ == "__main__": 16 | test_insertion_sort() 17 | -------------------------------------------------------------------------------- /Algorithm/Sorting/bubble_sort.py: -------------------------------------------------------------------------------- 1 | def bubble_sort(seq): 2 | length = len(seq) - 1 3 | for num in range(length, 0, -1): 4 | for idx in range(num): 5 | if seq[idx] > seq[idx + 1]: 6 | seq[idx], seq[idx + 1] = seq[idx + 1], seq[idx] 7 | return seq 8 | 9 | 10 | def test_bubble_sort(): 11 | seq = [4, 5, 2, 1, 6, 2, 7, 10, 13, 8] 12 | assert bubble_sort(seq) == sorted(seq) 13 | 14 | 15 | if __name__ == "__main__": 16 | test_bubble_sort() 17 | -------------------------------------------------------------------------------- /Algorithm/Sorting/count_sort.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | def count_sort(seq): 5 | b, c = [], defaultdict(list) 6 | for x in seq: 7 | c[x].append(x) 8 | for k in range(min(c), max(c) + 1): 9 | b.extend(c[k]) 10 | return b 11 | 12 | 13 | def test_count_sort(): 14 | seq = [3, 5, 2, 6, 8, 1, 0, 3, 5, 6, 2, 5, 4, 1, 5, 3] 15 | assert count_sort(seq) == sorted(seq) 16 | 17 | 18 | if __name__ == "__main__": 19 | test_count_sort() 20 | -------------------------------------------------------------------------------- /Data_structure/queue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Source code from Computer Science Bootcamp 3 | """ 4 | 5 | 6 | class Queue: 7 | def __init__(self): 8 | self._container = [] 9 | 10 | def enqueue(self, data): 11 | self._container.append(data) 12 | 13 | def dequeue(self): 14 | return self._container.pop(0) 15 | 16 | def empty(self): 17 | if not self._container: 18 | return True 19 | else: 20 | return False 21 | 22 | def peek(self): 23 | return self._container[0] 24 | -------------------------------------------------------------------------------- /Algorithm/Sorting/selection_sort.py: -------------------------------------------------------------------------------- 1 | def selection_sort(seq): 2 | length = len(seq) 3 | 4 | for i in range(length - 1): 5 | min_idx = i 6 | for j in range(i + 1, length): 7 | if seq[min_idx] > seq[j]: 8 | min_idx = j 9 | seq[i], seq[min_idx] = seq[min_idx], seq[i] 10 | return seq 11 | 12 | 13 | def test_selection_sort(): 14 | seq = [11, 3, 28, 43, 9, 4] 15 | assert selection_sort(seq) == sorted(seq) 16 | 17 | 18 | if __name__ == "__main__": 19 | test_selection_sort() 20 | -------------------------------------------------------------------------------- /Algorithm/Sorting/quick_sort.py: -------------------------------------------------------------------------------- 1 | def quick_sort(seq): 2 | if len(seq) < 2: 3 | return seq 4 | 5 | ipivot = len(seq) // 2 6 | pivot = seq[ipivot] 7 | 8 | left = [x for i, x in enumerate(seq) if x <= pivot and i != ipivot] 9 | right = [x for i, x in enumerate(seq) if x > pivot and i != ipivot] 10 | return quick_sort(left) + [pivot] + quick_sort(right) 11 | 12 | 13 | def test_quick_sort(): 14 | seq = [3, 5, 2, 6, 8, 1, 0, 3, 5, 6, 2] 15 | assert quick_sort(seq) == sorted(seq) 16 | 17 | 18 | if __name__ == "__main__": 19 | test_quick_sort() 20 | -------------------------------------------------------------------------------- /Data_structure/stack.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | def __init__(self): 3 | self._items = [] 4 | 5 | def is_empty(self): 6 | return not self._items 7 | 8 | def push(self, value): 9 | self._items.append(value) 10 | 11 | def pop(self): 12 | if not self.is_empty(): 13 | return self._items.pop() 14 | else: 15 | print("Stack is empty.") 16 | 17 | def size(self): 18 | return len(self._items) 19 | 20 | def peek(self): 21 | if not self.is_empty(): 22 | return self._items[-1] 23 | else: 24 | print("Stack is empty.") 25 | 26 | def __repr__(self): 27 | return repr(self._items) 28 | -------------------------------------------------------------------------------- /Algorithm/Sorting/merge_sort.py: -------------------------------------------------------------------------------- 1 | def merge_sort(seq): 2 | if len(seq) < 2: 3 | return seq 4 | 5 | mid = len(seq) // 2 6 | left, right = seq[:mid], seq[mid:] 7 | 8 | if len(left) > 1: 9 | left = merge_sort(left) 10 | if len(right) > 1: 11 | right = merge_sort(right) 12 | 13 | res = [] 14 | while left and right: 15 | if left[-1] >= right[-1]: 16 | res.append(left.pop()) 17 | else: 18 | res.append(right.pop()) 19 | res.reverse() 20 | 21 | return (left or right) + res 22 | 23 | 24 | def test_merge_sort(): 25 | seq = [3, 5, 2, 6, 8, 1, 0, 3, 5, 6, 2] 26 | assert merge_sort(seq) == sorted(seq) 27 | 28 | 29 | if __name__ == "__main__": 30 | test_merge_sort() 31 | -------------------------------------------------------------------------------- /Algorithm/binary_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | implementing binary search 3 | """ 4 | from typing import List, Union 5 | 6 | 7 | def binary_search(list_of_items: List[int], item: int) -> Union[int, None]: 8 | """Search index of target item in list of items 9 | 10 | Args: 11 | list_of_items : list of items 12 | item: target item 13 | 14 | Returns: index 15 | 16 | """ 17 | 18 | low = 0 19 | high = len(list_of_items) - 1 20 | 21 | while low <= high: 22 | mid = (low + high) // 2 23 | if item == list_of_items[mid]: 24 | return mid 25 | if item > list_of_items[mid]: 26 | low = mid + 1 27 | else: 28 | high = mid - 1 29 | return None 30 | 31 | 32 | def test_binary_search(): 33 | """test binary_search""" 34 | assert binary_search([1, 2, 3, 4, 5], 3) == 2 35 | assert binary_search([1, 2, 3, 4, 5], 10) is None 36 | -------------------------------------------------------------------------------- /Linux/Package manager.md: -------------------------------------------------------------------------------- 1 | # Package manager 2 | 3 | Linux 배포판에 따라 사용할 수 있는 Package manger(apt, yum 등)가 조금씩 다르지만 사용법은 대체로 비슷하다. 예를 들어, Linux 배포판 중 하나인 Ubuntu에서는 사용할 수 있는 Package manger로는 **apt** 가 있다. 4 | 5 | ## apt 6 | 7 | Ubuntu에서 package manager인 **apt** 를 활용하기 위해서는 일단 package manger인 **apt** 를 통해서 설치할 수 있는 package의 목록을 최신화하여야한다. (package의 목록을 download) 이 때 사용하는 명령어는 `apt-get update` 이다. 8 | 9 | ```bash 10 | $ apt-get update 11 | ``` 12 | 13 |  보통 root 권한이 아니라 user 권한으로 실행하고 있으므로 이때는 `sudo apt-get update` 를 해야한다. 14 | 15 | ```bash 16 | $ sudo apt-get update 17 | ``` 18 | 19 | `sudo apt-cache search` 뒤에 package 이름을 입력하면 해당 package와 함께 관련된 package를 모두 보여준다. 20 | 21 | ```bash 22 | $ sudo apt-cache search 23 | ``` 24 | 25 | `sudo apt-get install` 뒤에 package 이름을 입력하면 해당 package를 install 한다. `sudo apt-get upgrade` 뒤에 package 이름을 입력하면 해당 package를 최신화한다. 26 | 27 | ```bash 28 | $ sudo apt-get install 29 | ``` 30 | 31 | ```bash 32 | $ sudo apt-get upgrade 33 | ``` 34 | 35 | 만약 `apt-get upgrade` 명령을 특정 package를 명시하지않고 실행하면, `apt-get install` 로 설치한 모든 package를 upgrade한다. 36 | 37 | ```bash 38 | $ sudo apt-get upgrade 39 | ``` 40 | 41 | 설치한 package를 삭제하고 싶다면, `apt-get remove ` 42 | 43 | ```bash 44 | $ sudo apt-get remove 45 | ``` 46 | -------------------------------------------------------------------------------- /Data_structure/stack_with_node.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value=None, pointer=None): 3 | self._value = value 4 | self._pointer = pointer 5 | 6 | 7 | class Stack: 8 | def __init__(self): 9 | self._head = None 10 | self._count = 0 11 | 12 | def is_empty(self): 13 | return not self._count 14 | 15 | def push(self, value): 16 | self._head = Node(value, self._head) 17 | self._count += 1 18 | 19 | def pop(self): 20 | if not self.is_empty() and self._head: 21 | node = self._head 22 | self._head = node._pointer 23 | self._count -= 1 24 | return node._value 25 | else: 26 | print("Stack is empty.") 27 | 28 | def peek(self): 29 | if not self.is_empty() and self._head: 30 | return self._head._value 31 | else: 32 | print("Stack is empty.") 33 | 34 | def size(self): 35 | return self._count 36 | 37 | def __repr__(self): 38 | items = self._print_list() 39 | return repr(items) 40 | 41 | def _print_list(self): 42 | items = [] 43 | 44 | node = self._head 45 | while node: 46 | items.append(node._value) 47 | node = node._pointer 48 | 49 | items.reverse() 50 | 51 | return items 52 | -------------------------------------------------------------------------------- /CS/Variable.md: -------------------------------------------------------------------------------- 1 | # Variable 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | ## 1. 메모리 미리보기 4 | 컴퓨터는 1과 0으로 이루어진 데이터를 처리하며, 이때 1과 0을 표현할 수 있는 단위를 **비트(bit)** 라고 한다. 5 | **비트** 가 여덟 개 모이면 **바이트(byte)** 라고 부르며, 1바이트는 예를 들어, 01011010과 같이 0과 1이 총 여덟 개로 구성된다. 6 | ### 1.1 32비트와 64비트의 의미 7 | **비트** 는 컴퓨터가 사용하는 데이터 단위이므로, 몇 비트 컴퓨터이냐에 따라 한번에 데이터를 전송할 수 있는 양이 결정된다. 8 | 9 | * 8비트 컴퓨터 : 데이터를 한번에 8비트씩 전송할 수 있다. 메모리 주소를 8비트로 표현한다. 10 | * 32비트 컴퓨터 : 데이터를 한번에 32비트씩 전송할 수 있다. 메모리 주소를 32비트로 표현한다. 11 | * 64비트 컴퓨터 : 데이터를 한번에 64비트씩 전송할 수 있다. 메모리 주소를 64비트로 표현한다. 12 | 13 | ### 1.2 메모리에 우편번호를 메긴다. 14 | ***메모리 주소 한개는 메모리에서 1바이트를 가리킨다.*** 즉 8비트, 32비트, 64비트 컴퓨터에서는 각각 아래의 메모리를 활용할 수 있다. 15 | 16 | * 8비트 컴퓨터 : 8비트 컴퓨터는 $2^8=256$ 개의 메모리 주소를 가리킬 수 있으므로, ***256바이트의 메모리를 활용할 수 있다.*** 17 | * 32비트 컴퓨터 : 32비트 컴퓨터는 $2^{32}=4,294,967,296$ 개의 메모리 주소를 가리킬 수 있으므로, 4294967296바이트의 메모리를 활용할 수 있으며, $4,294,967,296 = 4 \times 1,024 \times 1,024 \times 1,024$ 이므로 ***4기가바이트의 메모리를 활용할 수 있다.*** 18 | * 64비트 컴퓨터 : 64비트 컴퓨터는 $2^{64} = 18,446,744,073,709,551,616$ 개의 메모리 주소를 가리킬 수 있으므로, ***4기가바이트 이상의 매우 큰 메모리를 활용할 수 있다.*** 19 | 20 | ## 2. 변수의 의미 21 | Python에서의 **변수(variable)** 는 일반적인 언어와 좀 다르지만, ***일반적인 언어에서는 변수는 데이터를 저장할 수 있는 메모리 공간 자체*** 를 의미한다. Python에서 **변수** 는 그저 이름일 뿐이며, 다른 메모리 공간에 있는 **값 객체(value object)** 를 가리킨다. 22 | 23 | ```python 24 | num = 5 25 | print(type(num)) 26 | ``` 27 | 28 | ```bash 29 | # int라는 class의 instance(object) 30 | ``` 31 | -------------------------------------------------------------------------------- /HTML-CSS/HTML_intermediates.md: -------------------------------------------------------------------------------- 1 | # HTML intermediates 2 | ### Intermediate tags 3 | #### table 4 | `` tag로 table을 생성하며 다음과 같은 tag를 활용하여, element를 만들 수 있다. 5 | 6 | * `` : table row를 생성, 아래의 두 가지 tag를 < `` tag안에는 다음의 tag를 사용할 수 있다. 7 | + `` : HTML상에서 table head에 해당하는 `` tag에 대하여 명시적으로 선언, 구별해주는 용도 10 | * `` : HTML상에서 table data에 해당하는 `` tag들에 대하여 명시적으로 선언, 구별해주는 용도 11 | 12 | ```xml 13 |
` : table head를 의미하며, table에서 column의 name을 명시하는 부분 8 | + `` : table data를 의미하며, table에서 column에 넣어줄 data를 넣는 부분 9 | * `
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | ``` 28 | 29 | #### Getting user inputs 30 | * `` : `` tag는 user의 input을 받을 때 사용하는 tag로 type attribute를 이용하여 input의 type을 결정한다. 31 | + `` 32 | + `` 33 | + `` 34 | + `` 35 | + `` 36 | * `
` : `` tag는 ***block level container로 user의 input을 getting 할 수 있는 여러 HTML tag들을 묶어서 관리할 때 사용한다.*** 아래의 두 개의 attribute를 주로 활용하며 더 자세한 내용은 아래의 link를 참고 37 | + link : 38 | + action : data를 어느 url로 보낼 지 39 | + method : HTTP의 어떤 method를 사용할 지 (get/post) 40 | 41 | -------------------------------------------------------------------------------- /Python/What is first-class function.md: -------------------------------------------------------------------------------- 1 | # What is first-class function 2 | Python에서는 function은 모두 **First-class function**으로 이는 function을 **First-class citizen**으로 취급하는 것을 의미한다. **First-class citizen**에 대한 상세한 내용은 Reference를 참고, 이 의미를 간단하게 요약해보자면 아래의 세 항목과 같다. 3 | 4 | * Reference 5 | + 6 | + 7 | 8 | **Summary** 9 | * First-class function (as First-class citizen) 10 | + function as argument 11 | + function as variable 12 | + function as return 13 | 14 | ### Function as argument 15 | function을 다른 function에 argument에 전달할 수 있다. 16 | 17 | ```python 18 | def foo(a,b): 19 | return a + b 20 | 21 | def bar(func, a, b): 22 | return func(a, b) 23 | 24 | result = bar(foo, 1, 2) 25 | print(result) 26 | ``` 27 | 28 | ```python 29 | 3 30 | ``` 31 | 32 | ### Function as variable 33 | function은 variable이므로 다른 variable로 가리킬 수 있다. 34 | 35 | ```python 36 | spam = foo 37 | 38 | result = spam(1, 2) 39 | print(result) 40 | ``` 41 | 42 | ```python 43 | 3 44 | ``` 45 | 46 | ### Function as return 47 | function을 다른 function에서 return 할 수 있다. 48 | 49 | ```python 50 | def calc_func_gen(kind): 51 | if kind=='add': 52 | def add(a, b): 53 | return a + b 54 | return add 55 | elif kind=='subtract': 56 | def subtract(a, b): 57 | return a - b 58 | return subtract 59 | 60 | adder = calc_func_gen('add') 61 | result = adder(1, 2) 62 | print(result) 63 | ``` 64 | 65 | ```python 66 | 3 67 | ``` 68 | -------------------------------------------------------------------------------- /Python/How to handle errors.md: -------------------------------------------------------------------------------- 1 | # How to handle errors 2 | `ValueError`, `FileNotFoundError`, `NameError`, `IndexError` 등등 여러가지 Error를 `try / except` 구문을 이용하여 처리하는 방법에 대해서 정리, 아래의 Reference를 참고함 3 | 4 | * Reference 5 | + 6 | 7 | ### try / except 기본 8 | `try / except` 구문은 아래와 같이 두 가지 경우에 대하여 사용할 수 있다. 9 | * 어떤 Error가 발생할 지 아는 경우 10 | * 어떤 Error가 발생할 지 모르는 경우 11 | 12 | 또한 `try / except` 구문 자체를 아래의 두 가지 형태로 활용할 수 있으며, 주로 ***usage 1의 형태가 usage 2의 형태보다 자주 활용된다.*** 목적에 맞게 활용하면 usage 2의 형태도 쓸모가 있다. 13 | 14 | #### usage 1 15 | ```python 16 | try: 17 | # error가 발생할 것 같은 code 18 | except: # except 뒤에 Error 종류 명시 가능 19 | # error가 발생했을 시 실행할 code 20 | ``` 21 | 22 | #### usage 2 23 | ```python 24 | try: 25 | # error가 발생할 것 같은 code 26 | except: # except 뒤에 Error 종류 명시 가능 27 | # error가 발생했을 시 실행할 code 28 | else: 29 | # try문 안의 code 대신 실행되면, 바로 이어서 실행할 code 30 | ``` 31 | 32 | ### try / except 활용 33 | #### 어떤 Error가 발생할 지 아는 경우 34 | 어떤 Error가 발생할 지 아는 경우에도 위와 같이 Error 종류를 명시하지않고 활용할 수 있으나, Error 종류를 알고 있다면 명시적으로 선언하고 아래의 예와 같이 활용한다. 35 | 36 | ```python 37 | # ZeroDivisionError가 발생되는 예제 38 | foo, bar = 1, 0 39 | 40 | try: 41 | result = foo / bar # ZeroDivisionError가 발생할 것이라는 것을 알고 있으므로 42 | except ZeroDivisionError as e: # e 변수로 해당 Error 발생 시 출력하는 Error message을 받아옴 43 | print('0으로 나눴습니다.') # Error 발생 시 실행할 코드 44 | print(e) # Error 발생 시 실행할 코드, ZeroDivisionError 발생 시 나오는 Error message를 출력 45 | ``` 46 | 47 | ```python 48 | 0으로 나눴습니다. 49 | division by zero 50 | ``` 51 | 52 | #### 어떤 Error가 발생할 지 모르는 경우 53 | 어떤 Error가 발생할 지 모르는 경우에도 위와 같이 Error 종류를 명시하지않고 활용할 수 있으나, `Exception`을 활용하고, 이를 variable로 받아서 Error message를 출력하는 형태로 활용한다. 54 | 55 | ```python 56 | # NameError가 발생되는 예제 57 | a = 1 58 | 59 | try: 60 | print(b) 61 | except Exception as e: 62 | print(e) 63 | ``` 64 | 65 | ```python 66 | name `b` is not defined 67 | ``` 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .DS_Store in mac 2 | .DS_Store 3 | 4 | # ipython notebook checkpoints 5 | .ipynb_checkpoints 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | 112 | # Pycharm project settings 113 | .idea 114 | -------------------------------------------------------------------------------- /Python/How to use abstract class.md: -------------------------------------------------------------------------------- 1 | # How to use abstract class 2 | Python에서 클래스(class)간의 관계를 정의할 때, 자식 클래스가 부모 클래스를 상속하는 경우 부모 클래스의 메소드를 무조건 **메소드 오버라이딩(method overriding)** 을 하게하고, 부모 클래스로부터 인스턴스(instance)를 생성못하게 하고 싶은 경우가 있다. 이 경우 **추상 클래스(abstract class)** 로 부모 클래스를 정의하고, 메소드를 정의할 시 `@abstractmethod`, `@abstractclassmethod` 등의 decorator를 활용한다. 3 | 4 | ### Template 5 | Python에서 추상 클래스를 정의할 시, `abc` module에서 `ABCMeta`, `abstractmethod`, `abstractclassmethod` 등을 가져와서 아래와 같이 사용한다. 6 | 보통 아래의 형태로 추상 클래스를 정의하며, `@abstractclassmethod` 와 `@abstractmethod` decorator로 꾸며진 메소드가 상속하는 자식 클래스에서 하나라도 재정의되지않으면, 추상 클래스로 취급되어 자식 클래스도 인스턴스를 생성할 수 없다. 7 | 8 | ```python 9 | from abc import ABCMeta, abstractmethod, abstractclassmethod 10 | class AbstractClass(metaclass = ABCMeta): 11 | 12 | @abstractclassmethod 13 | def class_method(cls): 14 | pass 15 | 16 | @abstractmethod 17 | def instance_method(self): 18 | pass 19 | ``` 20 | 21 | ```python 22 | try: 23 | test = AbstractClass() 24 | except TypeError as e: 25 | print(e) 26 | ``` 27 | 28 | ```bash 29 | Can't instantiate abstract class AbstractClass with abstract methods class_method, instance_method 30 | ``` 31 | 32 | ### Example : Animal class 33 | `Animal` 클래스를 추상 클래스로 정의하고, `Lion` , `Deer`, `Human` 클래스 등이 상속하는 예제 34 | 35 | ```python 36 | from abc import ABCMeta, abstractmethod, abstractclassmethod 37 | 38 | class Animal(metaclass = ABCMeta): 39 | 40 | def die(self): 41 | print('time to die') 42 | 43 | @abstractmethod # 자식클래스에서 반드시 재정의해야한다. 44 | def eat(self): # abstract instance method 45 | pass 46 | ``` 47 | 48 | ```python 49 | class Lion(Animal): 50 | def eat(self): 51 | print('eat meat') 52 | 53 | class Deer(Animal): 54 | def eat(self): 55 | print('eat grass') 56 | 57 | class Human(Animal): 58 | def eat(self): 59 | print('eat meat and grass') 60 | ``` 61 | 62 | ```python 63 | animals = [] 64 | lion = Lion() 65 | deer = Deer() 66 | human = Human() 67 | 68 | animals.append(lion) 69 | animals.append(deer) 70 | animals.append(human) 71 | 72 | for animal in animals: 73 | animal.eat() 74 | animal.die() 75 | ``` 76 | 77 | 78 | ```bash 79 | eat meat 80 | time to die 81 | eat grass 82 | time to die 83 | eat meat and grass 84 | time to die 85 | ``` -------------------------------------------------------------------------------- /Git/Git_github.md: -------------------------------------------------------------------------------- 1 | # Git github 2 | Remote repository로 [Github](https://github.com/)를 이용하는 방법을 정리 3 | 4 | * Reference 5 | + [지옥에서 온 Git](https://opentutorials.org/module/2676) 6 | + https://git-scm.com/book/en/v2 7 | + https://www.rlee.ai/apt/git 8 | + https://github.com/Gyubin/TIL/blob/master/ETC/git_gitflow.md 9 | --- 10 | 11 | ### Using Github as remote repository 12 | #### Preliminary 13 | * [Github](https://github.com/) 가입 14 | + [SSH public key 생성](https://git-scm.com/book/ko/v1/Git-%EC%84%9C%EB%B2%84-SSH-%EA%B3%B5%EA%B0%9C%ED%82%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0) 15 | + 에서 생성한 SSH public key를 등록 16 | 17 | #### Creating remote repository using Github 18 | Github에서 remote repository를 생성하면, 두 가지 방식으로 Github의 remote repository와 local repository를 연동할 수 있음 19 | 20 | * Github에서 remote repository를 생성하고, `git clone` 을 활용 (추천) 21 | 22 | ```bash 23 | # user_name : Github에서의 유저이름 24 | # repository_name : remote repository의 이름 25 | git clone git@github.com:user_name/repository_name.git 26 | ``` 27 | 28 | * Local에서 repository를 생성하고, remote repository와 연동 (이미 local repository에서 version control을 하고 있는 것을 remote repository로 backup 할 때 활용) 29 | 1. project directory를 `git init` 으로 version control 선언 (working directory 화) 30 | - ***이미 version control 하고 있던 project라면 위 과정 불필요*** 31 | 2. Github에 project directory의 이름과 같은 이름으로 remote repository 생성, 그 후 아래와 같이 32 | 33 | ```bash 34 | # origin은 뒷 부분 (remote repository의 alias) 35 | git remote add origin git@github.com:user_name/repository_name.git 36 | ``` 37 | 38 | #### Interlocking local repository with remote repository 39 | ##### push 40 | * local repository를 기준으로 하기 때문에, ***"local repository를 remote repository로 push한다."*** 라고 통칭함 41 | + local repository의 특정 branch의 version (commit)을 remote repository의 특정 branch로 push 42 | 43 | ```bash 44 | git push origin local_branch_name:remote_branch_name 45 | ``` 46 | 47 | * 일반적으로 local repository의 master branch를 remote repository의 master branch로 push하는 게 일반적, 따라서 아래와 같이 설정 48 | 49 | ```bash 50 | # local repository의 master branch를 remote repository의 master branch로 push하는 경우에는 51 | # 한번 아래의 명령어로 연결해놓으면 그 다음은 git push로 그냥 활용 52 | git push -u origin master 53 | ``` 54 | 55 | * remote repository에 없는 branch를 local repository에서 만들어서 push하는 경우, remote repository에 해당 branch가 생성됨 56 | 57 | ```bash 58 | git push origin branch_not_in_remote_repository 59 | ``` 60 | 61 | ##### pull 62 | * remote repository의 master branch를 local repository로 master branch로 가져오기 63 | 64 | ```bash 65 | git pull 66 | ``` 67 | 68 | * remote repository의 specific branch를 local repository의 specific branch로 가져오기 69 | 70 | ```bash 71 | git pull origin remote_branch_name:local_branch_name 72 | ``` 73 | -------------------------------------------------------------------------------- /HTML-CSS/HTML_basics.md: -------------------------------------------------------------------------------- 1 | # HTML basics 2 | ### HTML과 webpage를 구성하는 요소들 3 | **HTML (HyperText Markup Language)** 은 기본적으로 문서를 구조화해주는 지극히 간단한 언어, 웹사이트를 만드는 데에 있어서 아래와 같은 요소들이 모여 하나의 웹페이지를 구성하게된다. 요소와 역할은 아래와 같다. 4 | 5 | * html : language로 치면 noun, 뼈대를 잡는다. 6 | * css : language로 치면 adjective, noun을 꾸민다. 7 | * javascript : language로 치면 verb, webpage에 logic을 넣거나 interactivity를 부여한다. 8 | 9 | ### DOM 10 | **DOM (Document Object Model)** 에 대한 설명은 아래의 link를 참고 11 | 12 | * link : https://developer.mozilla.org/ko/docs/About_the_Document_Object_Model 13 | 14 | ### Boilerplate 15 | HTML 문서는 아래와 같은 기본 뼈대를 두고 만들어진다. 어떤 HTML 문서이든지 간에 아래의 양식은 항상 default 16 | 17 | * `` : `` tag에는 webpage의 header 정보를 넣는다. 아래의 두 가지 tag를 활용한다. 18 | + `` : `<title>`tag에는 웹페이지의 이름을 결정한다. browser 상에서 tab에 표시된다. 19 | + `<style>` : `<style>`tag에는 `<body>`tag안의 element들의 style을 지정한다. (css) 20 | * `<body>` : `<body>`tag는 webpage에 보여준 실제 내용을 포함한다. 여러 element들로 구성된다. 21 | 22 | ```xml 23 | <!DOCTYPE html> 24 | <html> 25 | <head> 26 | <title> 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | ### Basic tags 35 | * `` : `` tag안에는 아래의 여러 tag들을 이용해서 element들을 만들 수 있다. 36 | + heading : `

`, `

`, `

`, `

`, `

`, `
`를 활용하여 제목, 소제목 등을 지정 37 | + paragraph : `

`tag를 활용하여 paragraph를 명시적으로 지정가능 38 | + unordered list : `

    `tag를 활용하여, unordered list를 명시한 뒤, `
  • `tag를 활용하여 list를 만든다. 39 | 40 | ```xml 41 | 42 |
      43 |
    • 44 |
    • 45 |
    46 | ``` 47 | 48 | + ordered list : `
      `tag를 활용하여, ordered list를 명시한 뒤, `
    1. `tag를 활용하여 list를 만든다. 49 | 50 | ```xml 51 | 52 |
        53 |
      1. 54 |
      2. 55 |
      56 | ``` 57 | 58 | + `` : src attibute를 이용하여 image를 넣을 수 있다. 59 | 60 | ```xml 61 | 62 | ``` 63 | 64 | + `` : href attribute를 이용하여 linkㅇ를 생성할 수 있다. 65 | 66 | ```xml 67 | link url 68 | ``` 69 | ### Generic container 70 | `
      ` 와 `` 은 둘다 Generic conratiner로 customized structure를 만드는데 사용한다. (eg. element들을 모아서 일괄적으로 css 적용) 71 | 72 | Reference : 73 | 74 | * `
      ` : block level container로 주로 block level element를 구조화하는데 사용 75 | + ***block level element, block level container는 line당 하나만 들어갈 수 있음*** 76 | + link : 77 | * `` : inline level container로 주로 inline level element를 구조화하는데 사용 78 | + ***inline level element, inline level container는 line에 여러개 들어갈 수 있음*** 79 | + link : -------------------------------------------------------------------------------- /Data_structure/linked_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Source code from Computer Science Bootcamp 3 | """ 4 | 5 | 6 | class Node: 7 | def __init__(self, data): 8 | self._data = data 9 | self._next = None 10 | 11 | @property 12 | def data(self): 13 | return self._data 14 | 15 | @data.setter 16 | def data(self, data): 17 | self._data = data 18 | 19 | @property 20 | def next(self): 21 | return self._next 22 | 23 | @next.setter 24 | def next(self, node): 25 | self._next = node 26 | 27 | 28 | class LinkedList: 29 | def __init__(self): 30 | self._head = None 31 | self._tail = None 32 | self._size = 0 33 | 34 | def empty(self): 35 | return True if not self._size else False 36 | 37 | def size(self): 38 | return self._size 39 | 40 | def append(self, data): 41 | node = Node(data) 42 | 43 | if self.empty(): 44 | self._head = node 45 | self._tail = node 46 | self._size += 1 47 | else: 48 | self._tail.next = node 49 | self._tail = node 50 | self._size += 1 51 | 52 | def search_target(self, target, start=0): 53 | if self.empty(): 54 | return None 55 | 56 | position = 0 57 | current = self._head 58 | 59 | if position >= start and target == current.data: 60 | return current.data, position 61 | 62 | while current.next: 63 | position += 1 64 | current = current.next 65 | 66 | if position >= start and target == current.data: 67 | return current.data, position 68 | return None, None 69 | 70 | def search_position(self, position): 71 | if position > self.size() - 1: 72 | return None 73 | 74 | cnt = 0 75 | current = self._head 76 | if cnt == position: 77 | return current.data 78 | 79 | while cnt < position: 80 | current = current.next 81 | cnt += 1 82 | return current.data 83 | 84 | def remove(self, target): 85 | if self.empty(): 86 | return None 87 | 88 | before = self._head 89 | current = self._head 90 | 91 | if target == current.data: 92 | if self.size() == 1: 93 | self._head = None 94 | self._tail = None 95 | else: 96 | self._head = self._head.next 97 | self._size -= 1 98 | return current.data 99 | 100 | while current.next: 101 | before = current 102 | current = current.next 103 | if target == current.data: 104 | 105 | if current is self._tail: 106 | self._tail = before 107 | 108 | before.next = current.next 109 | self._size -= 1 110 | return current.data 111 | 112 | return None 113 | -------------------------------------------------------------------------------- /Python/How to use class method and static method.md: -------------------------------------------------------------------------------- 1 | # How to use class method and static method 2 | Python에서 **객체지향 프로그래밍(object-oriented programming, oop)** 을 구현하기위하여 `class` 를 활용할 때, class method와 static method를 활용하는 방법에 대하여 정리 3 | 4 | * **class method** 5 | + Python에서 `@classmethod` decorator를 이용하여 활용 6 | + Python에서 oop를 구현할 때 전역함수(global function)를 대체하기위해 사용할 때도 있지만, ***Python에서는 주로 `__init__` 생성자(constructor)를 만들 때 활용*** , Python에서의 type은 `method` 7 | + ***class variable과 마찬가지로 instance를 생성하지않고도 호출할 수 있으며, instance에서도 접근하거나 호출 할 수 있다.*** 8 | 9 | * **static method** 10 | + Python에서 `@staticmethod` decorator를 이용하여 활용 11 | + Python에서 oop를 구현할 때 전역함수(global function)를 대체하기위해 활용, Python에서 type은 `function` 12 | + ***class variable, class method 마찬가지로 instance를 생성하지않고도 호출할 수 있으며, instance에서도 접근하거나 호출 할 수 있다.*** 13 | 14 | ### Template 15 | Python에서 oop를 구현할 때, `@classmethod` `@staticmethod` decorator를 활용하는 것은 크게 아래의 형태를 벗어나지 않는다. `@classmethod` decorator를 이용하여 class method를 정의할 시, class method의 type은 `method` 이기 때문에 class 자기 자신에 대한 참조를 가지고 있다. 그리하여 class method 정의 시 첫 번째 argument는 `cls` 를 통상적으로 활용한다. (instance method 정의 시 첫 번째 argument로 통상적으로 `self` 를 쓰는 것과 같음) 16 | 17 | `@staticmethod` decorator를 이용하여 static method를 정의할 시, static method의 type은 `function` 이다. 이 때 argument를 비워놓거나 argument에 parameter로 instance, class를 전달하지 않는 것이 중요한 점이다. (단지 `class` 로 정의된 class 명의 namespace에 있을 뿐, 일반적인 전역함수와 같다.) 18 | 19 | ```python 20 | class Base: 21 | @staticmethod 22 | def f(): 23 | pass 24 | 25 | @classmethod 26 | def g(cls): 27 | pass 28 | 29 | # 이 밑에 instance method 들을 정의하거나 method overriding 30 | ``` 31 | 32 | ```python 33 | print(type(Base.f)) 34 | print(type(Base.g)) 35 | ``` 36 | 37 | ```bash 38 | 39 | 40 | ``` 41 | 42 | ### Example : Person class 43 | 44 | `Person` class에 `__init__` instance method 대신 다른 생성자(constructor)를 `@classmethod` 를 이용하여 class method `init_from_string` 를 정의하는 예제 45 | 46 | 47 | ```python 48 | class Person: 49 | 50 | __whole_population = 0 51 | 52 | @staticmethod 53 | def check_population(language): 54 | assert language in ['kor', 'eng'] 55 | if language == 'kor': 56 | return '현재 인구는 {}명 입니다.'.format(Person.__whole_population) 57 | else: 58 | return 'The population is {}'.format(Person.__whole_population) 59 | 60 | @classmethod 61 | def init_from_string(cls, string): 62 | ''' 63 | string format --> _ 64 | ''' 65 | name, age = string.split('_') 66 | age = int(age) 67 | return cls(name = name, age = age) 68 | 69 | @classmethod 70 | def __birth(cls): 71 | cls.__whole_population += 1 72 | 73 | 74 | def __init__(self, name, age): 75 | self.name = name 76 | self.age = age 77 | Person.__birth() 78 | 79 | def __str__(self): 80 | return '{} : {}'.format(self.name, self.age) 81 | ``` 82 | 83 | ```python 84 | string_list = ['greg_36', 'mark_24', 'john_12'] 85 | instances = [Person.init_from_string(string) for string in string_list] 86 | greg, mark, john = instances 87 | 88 | print(greg) 89 | print(mark) 90 | print(john) 91 | print(Person.check_population(language='kor')) 92 | ``` 93 | 94 | ```bash 95 | greg : 36 96 | mark : 24 97 | john : 12 98 | 현재 인구는 3명 입니다. 99 | ``` -------------------------------------------------------------------------------- /Data_structure/binary_tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Source code from Computer Science Bootcamp 3 | """ 4 | 5 | 6 | class TreeNode: 7 | def __init__(self): 8 | self._data = None 9 | self._left = None 10 | self._right = None 11 | 12 | def __del__(self): 13 | print('TreeNode of {} is deleted'.format(self._data)) 14 | 15 | @property 16 | def data(self): 17 | return self._data 18 | 19 | @data.setter 20 | def data(self, data): 21 | self._data = data 22 | 23 | @property 24 | def left(self): 25 | return self._left 26 | 27 | @left.setter 28 | def left(self, left): 29 | self._left = left 30 | 31 | @property 32 | def right(self): 33 | return self._right 34 | 35 | @right.setter 36 | def right(self, right): 37 | self._right = right 38 | 39 | 40 | class BinaryTree: 41 | def __init__(self): 42 | self._root = None 43 | 44 | def get_root(self): 45 | return self._root 46 | 47 | def set_root(self, node): 48 | self._root = node 49 | 50 | def make_node(self): 51 | new_node = TreeNode() 52 | return new_node 53 | 54 | def get_node_data(self, node): 55 | return node.data 56 | 57 | def set_node_data(self, node, data): 58 | node.data = data 59 | 60 | def get_left_subtree(self, node): 61 | return node.left 62 | 63 | def get_right_subtree(self, node): 64 | return node.right 65 | 66 | def make_right_subtree(self, node, right): 67 | node.right = right 68 | 69 | def make_left_subtree(self, node, left): 70 | node.left = left 71 | 72 | def preorder_traverse(self, node, func=None): 73 | if not node: 74 | return 75 | 76 | if func: 77 | func(node.data) 78 | else: 79 | print(node.data) 80 | 81 | self.preorder_traverse(node.left, func) 82 | self.preorder_traverse(node.right, func) 83 | 84 | def inorder_traverse(self, node, func=None): 85 | if not node: 86 | return 87 | 88 | self.inorder_traverse(node.left, func) 89 | 90 | if func: 91 | func(node.data) 92 | else: 93 | print(node.data) 94 | 95 | self.inorder_traverse(node.right, func) 96 | 97 | def postorder_traverse(self, node, func=None): 98 | if not node: 99 | return 100 | 101 | self.postorder_traverse(node.left, func) 102 | self.postorder_traverse(node.right, func) 103 | 104 | if func: 105 | func(node.data) 106 | else: 107 | print(node.data) 108 | 109 | 110 | if __name__ == '__main__': 111 | bt = BinaryTree() 112 | list_of_nodes = [bt.make_node() for _ in range(7)] 113 | 114 | for idx, node in enumerate(list_of_nodes, start=1): 115 | bt.set_node_data(node, idx) 116 | else: 117 | n1, n2, n3, n4, n5, n6, n7 = list_of_nodes 118 | bt.set_root(n1) 119 | bt.make_left_subtree(n1, n2) 120 | bt.make_right_subtree(n1, n3) 121 | bt.make_left_subtree(n2, n4) 122 | bt.make_right_subtree(n2, n5) 123 | bt.make_left_subtree(n3, n6) 124 | bt.make_right_subtree(n3, n7) 125 | 126 | # preorder traversal (node -> left subtree -> right subtree) 127 | bt.preorder_traverse(n1) 128 | # inorder traversal (left subtree -> node -> right subtree) 129 | bt.inorder_traverse(n1) 130 | # postorder traversal (left subtree -> right subtree -> node) 131 | bt.postorder_traverse(n1) 132 | -------------------------------------------------------------------------------- /CS/Character and Strings.md: -------------------------------------------------------------------------------- 1 | # Character and Strings 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | * Reference 5 | + 6 | + 7 | + 8 | 9 | ## 1. 아스키코드 10 | 아스키코드 절에 들어가기에 앞서 아래의 용어들을 기억 11 | 12 | * **문자 집합(character set)** : 문자(character)를 모아둔 집합, 13 | + eg. 라틴문자 14 | * **문자 인코딩(character encoding)** : 문자 집합을 메모리에 저장하거나 통신하는데 사용하기위해 부호화하는 방식 15 | + **코드 포인트(code point)** : 컴퓨터에 문자를 인식시키려면 문자를 정수에 매핑하고 이를 0과 1로 이루어진 2진수로 나타내야함. ***이때 매핑된 정수를 코드 포인트(code point)라고 함*** 16 | + **부호화된 문자 집합(coded character set, ccs)** : 문자마다 매핑된 코드 포인트를 모아놓은 집합 17 | - 아스키코드(ASCII) : 7비트 활용, 0~127까지의 코드 포인트 활용, 128개의 문자 18 | - 유니코드(UNICODE) : 16비트(2바이트) 활용, 0 ~ 65,535까지의 코드 포인트 활용, 65,536개의 문자 19 | 20 | * **문자 인코딩 방식(character encoding scheme, ces)** : 특정 부호화된 문자집합에서 나온 코드 포인트를 2진수로 바꾸었을 때, 이를 코드 유닛으로 인코딩하는 방법 21 | - eg. UTF-8, UTF-16, UTF-32, CP949, EUC-KR 22 | + **코드 유닛(code unit)** : 코드 포인트를 2진수로 변환하고 이를 특정 방법(eg. UTF-8)으로 인코딩했을 대 얻어지는 비트의 나열 23 | 24 | **아스키(american standard code for information interchange, ascii)** 는 대표적인 부호화된 문자 집합이면서 문자 인코딩 방식이다. (아스키는 특정한 코드 유닛을 만들지 않고 코드 포인트를 2진수로 바꾸어 그대로 메모리에 저장함) 아스키코드의 특징은 아래와 같다. 25 | 26 | * 7비트로 문자를 표현하므로 총 128개의 문자를 표현 가능 27 | * 코드 포인트는 0 ~127까지의 정수 28 | 29 | 예를 들어, 대문자 "A"는 10진수로 65, 16진수로는 0x41이다. 30 | 31 | ```python 32 | char = 'A' 33 | char = char.encode(encoding = 'ascii') 34 | print('code point : {}, hexadecimal: {}'.format(char[0], hex(char[0]))) 35 | ``` 36 | 37 | ```bash 38 | code point : 65, hexadecimal: 0x41 39 | ``` 40 | 41 | 아스키코드를 보면 정수 0부터 127까지 문자와 매핑되어있고, 2진수로 $0000 \ 0000_{(2)}$ 부터 $0111 \ 1111_{(2)}$ 이므로 최대 7비트가 필요하다. 정수를 나타내는 자료형은 바이트 수에 따라 다양하며, int형이 가장 자주 쓰인다. 일반적으로 32비트 컴퓨터에서는 아래와 같다. 42 | 43 | * 바이트 수에 따른 정수를 나타내는 자료형 (32비트, 64비트 동일) 44 | + int : 4바이트 45 | + short : 2바이트 46 | + char : 1바이트 47 | 48 | 위를 보았을 때, 아스키코드를 표현하는 데는 int, short 자료형을 사용하는 것은 메모리를 낭비하는 것이다. char 자료형은 문자를 담기 위해 만들어진 자료형이지만 정수 자료형이므로 작은 수를 표현하는 데 사용하기도 한다. 49 | 50 | ## 2. 유니코드 51 | **유니코드(unicode)** 는 16비트로 문자를 표현하여 65,536개의 문자를 표현할 수 있는 부호화된 문자 집합이다. 그 특징은 아래와 같다. 52 | 53 | * 아스키 코드를 포함한다. 기존 아스키코드에 $0000 \ 0000$을 붙임 54 | + $0000 \ 0000 \ 0000 \ 0000_{(2)} \sim 0000 \ 0000 \ 0111 \ 1111_{(2)}$ 이며 16진수로 표현하면 0x0000 ~ 0x007f 55 | * **기본 다국어 평면(basic multilingual plane, bmp)** 외에 여러 평면을 가지고 있다. 56 | + 기본 다국어 평면은 65,536개의 코드 포인트를 가지고 있고, 여기에 한글 및 많은 언어의 문자가 매핑되어있다. 57 | + U+0000 ~ U+FFFF 58 | 59 | 코드로 한글의 범위를 확인해보면 다음과 같다. 60 | 61 | ```python 62 | print('\uac00') 63 | ``` 64 | 65 | ```bash 66 | 가 67 | ``` 68 | 69 | ## 3. 유니코드기반 인코딩 방식 70 | ### 3.1 UTF-8 71 | **UTF-8(universal coded character set transformation format - 8bit)** 은 유니코드 기반의 인코딩 방식 중 하나로 유니코드 문자 하나를 1바이트에서 4바이트 사이에서 표현한다. 그 특징은 아래와 같다. 72 | 73 | * 가변 길이 인코딩 방식으로 유니코드에서 코드포인트에 따라 인코딩의 결과물인 코드유닛의 바이트가 다르다. 74 | + U+0000 ~ U+007F : 1바이트 75 | + U+0080 ~ U+07FF : 2바이트 76 | + U+0800 ~ U+FFFF : 3바이트, 한글이 여기에 해당됨 77 | + 그 외 4바이트 78 | * endianless 방식이다. 79 | 80 | 유니코드에서 한글 "가"의 코드포인트는 U+AC00으로 UTF-8로 인코딩하면 ea b0 80이다. 코드로 확인하면 아래와 같다. 81 | 82 | ```python 83 | char = '\uac00' 84 | print(char) 85 | print(char.encode(encoding = 'utf-8')) 86 | ``` 87 | 88 | ```bash 89 | 가 90 | b'\xea\xb0\x80' 91 | ``` 92 | 한글 "가"의 유니코드 코드포인트에서 UTF-8로 인코딩 과정은 아래와 같다. 93 | 94 | 1. "가" 코드 포인트 U+ac00을 2진수로 표현한다. 95 | $$ 96 | 1010 \ 1100 \ 0000 \ 0000_{(2)} 97 | $$ 98 | 99 | 2. UTF-8에서 유니코드 코드 포인트가 U+0800에서 U+FFFF인 문자는 다음의 포맷으로 인코딩하여 코드 유닛을 생성한다. 100 | $$ 101 | 1110XXXX \ 10XXXXXX \ 10XXXXXX 102 | $$ 103 | 104 | 3. 위의 포맷에 맞추어 1에서 2진수로 표현한 것을 X에 앞에서부터 차례로 채워넣는다. 105 | $$ 106 | 11101010 \ 10110000 \ 10000000 107 | $$ 108 | 109 | 4. 3의 2진수를 16진수로 표현하면 0xeab080 110 | 111 | ## 3.2 UTF-16 112 | **UTF-16** 은 유니코드 기반의 인코딩 방식 중 하나로 2바이트 단위로 문자를 표현한다. 해당 문자가 기본 다국어 평면에 있으면 2바이트로 인코딩되고, 그렇지 않으면 4바이트로 인코딩한다. 113 | 114 | ## 3.3 UTF-32 115 | **UTF-32** 는 유니코드 인코딩 방식의 하나로 모든 문자를 4바이트로 인코딩한다. 116 | -------------------------------------------------------------------------------- /Python/How to use iterator and generator.md: -------------------------------------------------------------------------------- 1 | # How to use iterator and generator 2 | Python에서 **Lazy evaluation** 을 활용하기위해서는 `iterator` object를 활용해야하며, `generator` 는 `function` object이면서 `iterator` object이다. `iterator` object에 해당하는 object는 `generator` object외에도 `map` object, `filter` object가 있으며, 이들은 주로 anonymous function을 선언하는 `lambda` 키워드와 함께 `map`, `filter` 키워드가 사용되어 생성되며, **Lazy evaluation** 을 한다. (`reduce` 는 엄밀한 의미에서는 **Lazy evaluation** 은 아니다.) 해당 내용은 Reference에 자세히 정리되어있다. 3 | 4 | * Reference 5 | + [How to use functional programming](https://aisolab.github.io/programming%20language/2018/08/07/Python_How-to-use-functional-programming/) 6 | 7 | ### Summary 8 | `iterator` object와 `generator` object, 더불어 `iterator` object와 면밀한 관계가 있는 `iterable` object를 간략하게 정리하면 아래와 같다. 9 | 10 | * `iterable` object 11 | + `__iter__` method가 정의되어있는 sequential data type의 object 12 | + `iter` function의 해당 `iterable` object를 넣으면, object의 element를 모두 가지고 있는 `iterator` object를 생성함 (eg. `list_iterator`, `str_iterator`, `tuple_iterator`) 13 | + `for` loop으로 순회가능하며, `iterable` object를 `for` loop로 순회하면 내부적으로 해당 `iterable` object를 기반으로 `iterator` object를 생성하여 `next` function을 적용하는 것과 같음 14 | 15 | * `iterator` object 16 | + `__next__` method와 `__iter__` method가 정의되어있는 sequential data type의 object 17 | + 해당 object를 `for` loop로 한번 순회하면 그 이후 순회해도 object의 element에 접근불가 18 | + `next` function으로 object의 element를 다 꺼내면 `StopIteration` error 발생 19 | 20 | * `generator` object 21 | + `function` object 이면서 `iterator` object의 일종 22 | + function을 정의시 `return` 대신 `yield` 를 사용하여 `generator` object 생성, `return` 은 function을 바로 종료시키는 반면 `yield` 는 그렇지 않다. 23 | 24 | ### Template 25 | * `iterator` object 26 | `class` 를 이용해서 `iterator` object를 정의하고 싶을 경우 일반적으로 아래와 같이 정의한다. 27 | 28 | ```python 29 | class It: 30 | def __init__(self, *args): 31 | self.data = args 32 | self.idx = 0 33 | 34 | def __iter__(self): # __iter__ method 정의시 그냥 자기자신을 return 35 | return self 36 | 37 | def __next__(self): 38 | 39 | if self.idx >= len(self.data): 40 | raise StopIteration 41 | else: 42 | elm = self.data[self.idx] 43 | self.idx += 1 44 | return elm 45 | ``` 46 | 47 | ```bash 48 | 1 49 | 2 50 | 3 51 | 4 52 | iterator가 element를 다 꺼냈습니다. 53 | ``` 54 | 55 | * `generator` object 56 | 57 | ```python 58 | def gen(): 59 | yield 1 60 | yield 2 61 | yield 3 62 | yield 4 63 | ``` 64 | 65 | ```python 66 | generator = gen() 67 | print(type(generator)) 68 | 69 | try: 70 | while True: 71 | print(next(generator)) 72 | except StopIteration: 73 | print('StopIterator error 발생') 74 | ``` 75 | 76 | ```bash 77 | 78 | 1 79 | 2 80 | 3 81 | 4 82 | StopIterator error 발생 83 | ``` 84 | 85 | ### Example : Fibonacci number 86 | * Non generator 87 | 88 | ```python 89 | # non generator 90 | def make_fibo(n): 91 | result = [] 92 | a = 0 93 | b = 1 94 | for i in range(n): 95 | result.append(a) 96 | a, b = b, a+b 97 | else: 98 | return result 99 | 100 | result = make_fibo(10) 101 | print(result) 102 | ``` 103 | 104 | ```bash 105 | [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 106 | ``` 107 | 108 | * Generator 109 | 110 | ```python 111 | # generator 112 | def make_fibo(n): 113 | a = 0 114 | b = 1 115 | 116 | for _ in range(n): 117 | yield a 118 | a, b = b, a+b 119 | 120 | gen = make_fibo(10) 121 | 122 | result = [] 123 | 124 | try: 125 | while True: 126 | result.append(next(gen)) 127 | except StopIteration: 128 | print(result) 129 | ``` 130 | 131 | ```bash 132 | [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 133 | ``` 134 | 135 | * Bonus : Recursive call 136 | 137 | ```python 138 | # recursion call 139 | def make_fibo(n): 140 | if n == 1: 141 | return 0 142 | elif n == 2 : 143 | return 1 144 | else: 145 | return make_fibo(n-1) + make_fibo(n-2) 146 | 147 | result = [make_fibo(i) for i in range(1,11)] 148 | print(result) 149 | ``` 150 | 151 | 152 | ```bash 153 | [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 154 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Today I Learned 2 | 3 | Aisolab이 오늘 새로 배운 것을 다음의 규칙으로 commit 합니다. [thoughtbot til 참고](https://github.com/thoughtbot/til) 4 | 5 | ## 작성 규칙 6 | * 문서 생성은 [GFM (Github Flavored Markdown)](https://help.github.com/articles/github-flavored-markdown/) 을 사용한다. (확장자 `.md`) 7 | + code 실행결과가 필요한 문서의 경우 `.ipynb` 파일을 활용한다. 단 `README.md`에 link 시 [nbviewer](https://nbviewer.jupyter.org/)를 활용한다. 8 | * 언어나 기술명으로 폴더를 만든다. (root에 문서를 만들지 않는다.) 9 | * 파일명은 영어로! 10 | * [좋은 git commit message를 작성하기 위한 7가지 약속](http://meetup.toast.com/posts/106) 11 | 12 | --- 13 | ### CS 14 | * [Variable.md](https://github.com/aisolab/TIL/blob/master/CS/Variable.md) 15 | * [Integer.md](https://github.com/aisolab/TIL/blob/master/CS/Integer.md) 16 | * [Real number.md](https://github.com/aisolab/TIL/blob/master/CS/Real%20number.md) 17 | * [Character and Strings.md](https://github.com/aisolab/TIL/blob/master/CS/Character%20and%20Strings.md) 18 | * [Function.md](https://github.com/aisolab/TIL/blob/master/CS/Function.md) 19 | * [Object-oriented programming.md](https://github.com/aisolab/TIL/blob/master/CS/Object-oriented%20programming.md) 20 | * [Class.md](https://github.com/aisolab/TIL/blob/master/CS/Class.md) 21 | 22 | ### Git 23 | * [Git_basics.md](https://github.com/aisolab/TIL/blob/master/Git/Git_basics.md) 24 | * [Git_github.md](https://github.com/aisolab/TIL/blob/master/Git/Git_github.md) 25 | 26 | ### HTML-CSS 27 | * [HTML_basics.md](https://github.com/aisolab/TIL/blob/master/HTML-CSS/HTML_basics.md) 28 | * [HTML intermediates.md](https://github.com/aisolab/TIL/blob/master/HTML-CSS/HTML_intermediates.md) 29 | 30 | ### Linux 31 | * [Package manager.md](https://github.com/aisolab/TIL/blob/master/Linux/Package%20manager.md) 32 | 33 | ### Python 34 | * [What is first-class function.md](https://github.com/aisolab/TIL/blob/master/Python/What%20is%20first-class%20function.md) 35 | * [How to handle errors.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20handle%20errors.md) 36 | * [How to use closure.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20closure.md) 37 | * [How to use decorator.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20decorator.md) 38 | * [How to use functional programming.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20functional%20programming.md) 39 | * [How to use argparse module.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20argparse%20module.md) 40 | * [How to use class method and static method.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20class%20method%20and%20static%20method.md) 41 | * [How to implement information hiding.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20implement%20information%20hiding.md) 42 | * [How to use iterator and generator.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20iterator%20and%20generator.md) 43 | * [How to use abstract class.md](https://github.com/aisolab/TIL/blob/master/Python/How%20to%20use%20abstract%20class.md) 44 | 45 | ### Tensorflow 46 | * [How to simply use tf.data.ipynb](https://nbviewer.jupyter.org/github/aisolab/TIL/blob/master/Tensorflow/How%20to%20simply%20use%20tf.data.ipynb) 47 | * [How to implement reusing variables.ipynb](https://nbviewer.jupyter.org/github/aisolab/TIL/blob/master/Tensorflow/How%20to%20implement%20reusing%20variables.ipynb) 48 | * [How to use TFRecord format.ipynb](https://nbviewer.jupyter.org/github/aisolab/TIL/blob/master/Tensorflow/How%20to%20use%20TFRecord%20format.ipynb) 49 | * [How to use pre-trained models.ipynb](https://nbviewer.jupyter.org/github/aisolab/TIL/blob/master/Tensorflow/How%20to%20use%20pre-trained%20models.ipynb) 50 | * [How to implement data augmentation.ipynb](https://nbviewer.jupyter.org/github/aisolab/TIL/blob/master/Tensorflow/How%20to%20implement%20data%20augmentation.ipynb) 51 | 52 | ### Clean code 53 | * [CC01_Introduction, Tools and Formatting.md](https://github.com/aisolab/TIL/blob/master/Clean_code/CC01_Introduction%2C%20Tools%20and%20Formatting.md) 54 | 55 | ### Data structure 56 | 57 | 58 | ### Algorithm 59 | * [bubble_sort.py](https://github.com/aisolab/TIL/blob/master/Algorithm/bubble_sort.py) 60 | * [selection_sort.py](https://github.com/aisolab/TIL/blob/master/Algorithm/selection_sort.py) 61 | * [quick_sort.py](https://github.com/aisolab/TIL/blob/master/Algorithm/quick_sort.py) 62 | -------------------------------------------------------------------------------- /Python/How to implement information hiding.md: -------------------------------------------------------------------------------- 1 | # How to implement information hiding 2 | Python에서 **객체지향 프로그래밍(object-oriented programming, oop)** 으로 프로그램을 작성 시, value object와 function object를 하나의 단위로 묶고, 더불어 구현 내용 중 일부를 감추는 **정보 은닉(information hiding)** 을 하는 **캡슐화(encapsulation)** 를 `class` 를 이용해서 구현하나, 정보 은닉을 위해서 C++에서 `public` , `pricate` 등의 접근 제어 지시자(access modifier)를 지원하는 반면에 Python에서는 애초에 정보 은닉을 지원하지 않는다. Python에서 정보 은닉을 비슷하게 구현하기위해서 아래 의 세 가지 정도를 활용한다. 3 | 4 | * Reference 5 | + 6 | 7 | ### Summary 8 | * **`_` 를 사용** 9 | + class variable, class method, instance variable, instance method 등을 정의 시, `_` 를 variable명 또는 method명에 붙여 정의함으로써 private임을 명시,**프로그래머 간에 수정하지않는 variable 또는 method라고 약속, class 외부에서 접근하기 힘들게함** 10 | * **name mangling** 11 | + class variable, class method, instance variable, instance method 등을 정의 시, `__` 를 variable명 또는 method명에 붙여 정의 12 | + class 내부에서는 `__variable명` 또는 `__method명` 으로 접근 가능하나 class 외부에서는 `_class명__variable명`, `__class명__method명` 으로만 접근이 가능 13 | * **property 기법** 14 | + `@property` decorator를 활용하는 방법으로 `@property` decorator를 이용하면 instance variable, class variable을 호출하는 것 같지만 실제로는 instance method, class method를 호출하도록 만들 수 있다. 15 | 16 | ### Example : Account class 17 | `Account` class를 **Summary**에 기술한 세 가지 방법 중 `_` 을 사용하는 경우는 사실상 프로그래머간의 약속이므로 예제에서 제외하고, **name mangling** , **property 기법** 을 사용하는 예제를 보면 아래와 같다. 18 | 19 | * **name mangling** 20 | 21 | ```python 22 | class Account: 23 | def __init__(self, name, money): 24 | self.__name = name 25 | self.__balance = money 26 | 27 | def get_balance(self): 28 | return self.__balance 29 | 30 | def set_balance(self, new_bal): 31 | if new_bal < 0: 32 | return 33 | self.__balance = new_bal 34 | 35 | aisolab = Account(name = 'aisolab', money = 5000) 36 | print(aisolab.__dict__) 37 | ``` 38 | 39 | ```bash 40 | {'_Account__name': 'aisolab', '_Account__balance': 5000} 41 | ``` 42 | 43 | ```python 44 | # 잔고를 음수를 설정할 수 없다. 45 | aisolab.__balance = -5000 46 | print(aisolab.__dict__) 47 | print(aisolab._Account__balance) 48 | 49 | aisolab.set_balance(-7000) 50 | print(aisolab.__dict__) 51 | 52 | # 완벽한 정보은닉이 아니기 때문에 실제 외부에서 접근할 수 있는 이름을 알면 설정할 수 있다. 53 | aisolab._Account__balance = -5000 54 | print(aisolab.__dict__) 55 | ``` 56 | 57 | ```bash 58 | {'_Account__name': 'aisolab', '_Account__balance': 5000, '__balance': -5000} 59 | 5000 60 | {'_Account__name': 'aisolab', '_Account__balance': 5000, '__balance': -5000} 61 | {'_Account__name': 'aisolab', '_Account__balance': -5000, '__balance': -5000} 62 | ``` 63 | 64 | * **property 기법** 65 | 66 | ```python 67 | class Account: 68 | def __init__(self, name, money): 69 | self.__name = name 70 | self.balance = money 71 | 72 | @property 73 | def balance(self): # getter function 74 | return self.get_balance() 75 | 76 | @balance.setter 77 | def balance(self, new_bal): # setter function 78 | self.set_balance(new_bal) 79 | 80 | def get_balance(self): # original getter function 81 | return self.__balance 82 | 83 | def set_balance(self, new_bal): # original setter function 84 | if new_bal < 0: 85 | pass 86 | else: 87 | self.__balance = new_bal 88 | 89 | aisolab = Account(name = 'aisolab', money = 5000) 90 | print(aisolab.__dict__) 91 | ``` 92 | 93 | ```bash 94 | {'_Account__name': 'aisolab', '_Account__balance': 5000} 95 | ``` 96 | 97 | ```python 98 | # 인스턴스 변수에 접근한 것 같지만 사실은 @property가 적용된 99 | # balance()가 실행되어 return 값을 본 것 100 | print(aisolab.balance) 101 | ``` 102 | 103 | ```bash 104 | 5000 105 | ``` 106 | 107 | ```python 108 | # 인스턴스 변수를 직접 수정한 것 같지만 @blance.setter가 적용된 blance()가 실행된 것 109 | aisolab.balance = -20 110 | print(aisolab.get_balance()) 111 | 112 | aisolab.balance = 50 113 | print(aisolab.get_balance()) 114 | ``` 115 | 116 | ```python 117 | 5000 118 | 50 119 | ``` 120 | 121 | ```python 122 | # 완벽한 정보은닉이 아니기 때문에 실제 외부에서 접근할 수 있는 이름을 알면 설정할 수 있다. 123 | aisolab._Account__balance = -5000 124 | print(aisolab.__dict__) 125 | print(aisolab.get_balance()) 126 | ``` 127 | 128 | ```bash 129 | {'_Account__name': 'aisolab', '_Account__balance': -5000} 130 | -5000 131 | ``` 132 | -------------------------------------------------------------------------------- /Python/How to use argparse module.md: -------------------------------------------------------------------------------- 1 | # How to use argparse module 2 | argparse module은 command line parsing module로써 CLI program 또는 tool을 만들 때 사용하며, 아래와 같은 방법으로 module을 활용한다. 상세한 내용은 아래의 Reference를 참고 3 | 4 | * Reference 5 | + 6 | + 7 | + 8 | + 9 | 10 | ### Parser 생성 11 | argparse module을 이용하여 parser instance를 생성한다. 12 | * `argparse.ArgumentParser` : ***ArgumentParser class의 instance를 생성, 필요시 여러 argument에 값을 전달하나 아래와 같이만 활용해도 충분하다.*** instance의 method를 이용하여 실제로 parsing을 함 13 | 14 | ```python 15 | import argparse 16 | parser = argparse.ArgumentParser() # parser instance를 생성 17 | ``` 18 | 19 | ### Parser 활용 20 | 생성된 parser instance의 method를 활용하여 CLI를 통해 입력받는 parameter를 parsing한다. 21 | 22 | ***notice*** 23 | 1. 아래의 설명에서 argument로 통칭할 때는 positional argument와 optional argument를 둘다 가리킴 24 | 2. 아래의 설명에서의 variable의 name은 `args = parser.parse_args()`에서 `args`의 member variable의 name을 의미 25 | 26 | * `argparse.ArgumentParser.add_argument` : CLI에서 입력받을 argument를 설정한다. 해당 method에는 여러 argument가 있지만 중요한 몇 개는 아래와 같다. 27 | + `name of flags` : argument를 등록한다. postional argument와 optional argument로 나누어져 있으며 아래와 같은 특징이 있음 28 | - positional argument : `name of flags`에 `-`없이 값을 전달하며, `dest` argument에 variable의 name을 전달할 수 없다. 이는 `name of flags`라는 의미대로 positional argument를 활용할 때, 바로 variable name이 되기 때문 29 | ```python 30 | # test.py 31 | import argparse 32 | parser = argparse.ArgumentParser() 33 | parser.add_argument('p') 34 | args = parser.parse_args() 35 | print(args) 36 | ``` 37 | ```bash 38 | $ python test.py -h 39 | usage: test.py [-h] p 40 | 41 | positional arguments: 42 | p 43 | 44 | optional arguments: 45 | -h, --help show this help message and exit 46 | ``` 47 | ```bash 48 | $ python test.py hello 49 | Namespace(p='hello') 50 | ``` 51 | 52 | - optional argument : `name of flags`에 `-`, `--`를 붙여 값을 전달하며, 해당 method의 `dest` argument에 값을 전달하여 명시적으로 variable의 name을 결정할 수 있다. `dest` argument에 값을 전달하지않으면, `--string` 에서 string에 해당하는 문자열을 variable의 name으로 한다. 53 | ```python 54 | # test.py 55 | import argparse 56 | parser = argparse.ArgumentParser() 57 | parser.add_argument('-o', '--optional', dest = 'o', type = str) 58 | args = parser.parse_args() 59 | print(args) 60 | ``` 61 | ```bash 62 | $ python test.py -h 63 | usage: test.py [-h] [-o O] # -o O에 O는 O에 해당하는 부분에 값을 전달하라는 것 64 | 65 | optional arguments: 66 | -h, --help show this help message and exit 67 | -o O, --optional O 68 | ``` 69 | ```bash 70 | $ python test.py -o hello 71 | Namespace(o='hello') 72 | ``` 73 | 74 | + `nargs` : argument가 전달받을 수 있는 값의 개수를 가리킴. 이 값보다 많은 값이 들어오는 경우는 무시되며, `+`를 전달할 경우 1개 이상의 값을 받는다는 의미이다. 전달받은 여러 값은 list의 형태로 저장 75 | + `default` : optional argument를 추가할 때, 해당 optional argument가 필수적으로 값을 전달받아야할 argument가 아닐 경우, `default` argument에 값을 전달하면 말그대로 default 값을 설정가능 76 | + `type` : argument에 전달받은 parameter를 parsing하여 저장할 때, type을 설정 77 | + `choices` : argument가 받을 수 있는 값의 목록을 list형태로 전달, argument에 전달받은 값이 list의 element 중 하나가 아니면 error 발생 78 | + `help` : `-h` 또는 `--help` option으로 script를 실행했을 때, argument에 대한 설명을 설정 79 | + `required` : optional argument인 경우, default 값은 False이며 필수적으로 값을 받아야할 optional argument인 경우 True로 설정, 없으면 알아서 error message를 표시하고 자동으로 exit (positional argument일 때는 해당 argument를 사용하지 않음) 80 | + `metavar` : usage message를 출력할 때 표시할 name을 지정 (`dest` argument에 전달한 name보다 우선순위가 높음, 하지만 딱히 쓰지않는 게 좋을 듯하다.) 81 | + `dest` : optional argument를 사용 시 variable의 name을 명시적으로 지정할 때 사용 82 | * `argparse.ArgumentParser.parse_args` : argument에 전달받은 parameter를 parsing하여, 각각의 parameter를 member variable로 갖는 `argparse.Namespace` class의 instance를 생성한다. 83 | ```python 84 | # test.py 85 | import argparse 86 | parser = argparse.ArgumentParser(description = 'test program입니다.') 87 | parser.add_argument('p', type = str, help = 'postional argument입니다.') 88 | parser.add_argument('-o', '--optional', type = str, dest = 'o', help = 'optional argument입니다.') 89 | args = parser.parse_args() 90 | print(args) 91 | print(args.p) 92 | print(args.o) 93 | ``` 94 | ```bash 95 | $ python test.py -h 96 | usage: test.py [-h] [-o O] p 97 | 98 | test program입니다. 99 | 100 | positional arguments: 101 | p postional argument입니다. 102 | 103 | optional arguments: 104 | -h, --help show this help message and exit 105 | -o O, --optional O optional argument입니다. 106 | ``` 107 | ```bash 108 | $ python test.py hi -o hello 109 | Namespace(o='hello', p='hi') # print(args) 110 | hi # print(args.p) 111 | hello # print(args.o) 112 | ``` -------------------------------------------------------------------------------- /Python/How to use decorator.md: -------------------------------------------------------------------------------- 1 | # How to use decorator 2 | Python에서 function에 기능을 추가할 때, ***실제로는 추가할 기능을 가지고 있으며, function을 입력으로 전달받는 specific function을 선언하여 활용하게된다.*** 하지만 Python에서 지원하는 **decorator keyword**인 `@` keyword를 이용하면 아주 손쉽게 기능을 추가할 수 있다. 3 | 4 | * Reference 5 | + 6 | 7 | ### Template 8 | **decorator** 기능을 활용하는 것은 크게 아래의 형태를 벗어나지 않는다. 9 | 10 | * decorator function을 생성 11 | ```python 12 | def decorator_func(func): 13 | def __wrapper(*args, **kwargs): 14 | # positional argument들은 packing하여 local variable args에 받는다 15 | # keyword argument들은 packing하여 local variable kwargs에 받는다. 16 | print('things to do') # 추가하고 싶은 기능(실행문)을 작성 17 | return func(*args, **kwargs) 18 | # return할 때 local variable args, kwargs unpacking해서 기능을 추가하고자하는 19 | # function에 전달한다. 20 | return __wrapper # decorator_func은 __wrapper func은 return한다. 21 | ``` 22 | * 기능을 추가하고 싶은 function에 decorator function을 활용하여 기능 추가 23 | + 기능을 추가하고 싶은 function 24 | ```python 25 | def add(a,b): 26 | '''To add a and b 27 | 28 | Args: 29 | a : int or numeric type 30 | b : int or numeric type 31 | Returns: 32 | a + b : int or numeric type 33 | ''' 34 | return a + b 35 | 36 | print(add.__name__) 37 | print(add.__doc__) 38 | ``` 39 | 40 | ```python 41 | add 42 | To add a and b 43 | 44 | Args: 45 | a : int or numeric type 46 | b : int or numeric type 47 | Returns: 48 | a + b : int or numeric type 49 | ``` 50 | 51 | + decorating func without `@` keyword 52 | ```python 53 | add = decorator_func(add) 54 | result = add(1,3) 55 | print(result) 56 | print(add.__name__) 57 | print(add.__doc__) 58 | ``` 59 | 60 | ```python 61 | # add function에 print('things to do') 기능이 잘 추가되었으나, decorator_func의 return이 __wrapper이므로 add.__name__, add.__doc.__이 각각 __wrapper.__name__, __wrapper.__doc__을 가리키게 됨 62 | things to do 63 | 4 64 | __wrapper 65 | None 66 | ``` 67 | 68 | + decorating func with `@` keyword 69 | ```python 70 | @decorator_func 71 | def add(a,b): 72 | return a + b 73 | 74 | result = add(1,3) 75 | print(result) 76 | print(add.__name__) 77 | print(add.__doc__) 78 | ``` 79 | 80 | ```python 81 | # add function에 print('things to do') 기능이 잘 추가되었으나, decorator_func의 return이 __wrapper이므로 add.__name__, add.__doc.__이 각각 __wrapper.__name__, __wrapper.__doc__을 가리키게 됨 82 | things to do 83 | 4 84 | __wrapper 85 | None 86 | ``` 87 | 88 | 위의 template과 같이 `@` keyword를 활용하면, 매우 손쉽게 `add` function에 기능을 추가할 수있으나, `add` function의 `add.__name__` 과 `add.__doc__`이 `decorator_func` function의 내부 function인 `__wrapper` function의 `__wrapper.__name__`과 `__wrapper.__doc__`을 가리키게되며, 이 같은 문제를 해결하기위해서는 `decorator_func`을 정의할 때, `functools` module에서 `wraps` function을 불러워서 아래와 같이 활용하면 해결 가능 89 | 90 | ```python 91 | from functools import wraps 92 | def decorator_func(func): 93 | @wraps(func) 94 | def __wrapper(*args, **kwargs): 95 | # positional argument들은 packing하여 local variable args에 받는다 96 | # keyword argument들은 packing하여 local variable kwargs에 받는다. 97 | print('things to do') # 추가하고 싶은 기능(실행문)을 작성 98 | return func(*args, **kwargs) 99 | # return할 때 local variable args, kwargs unpacking해서 기능을 추가하고자하는 100 | # function에 전달한다. 101 | return __wrapper # decorator_func은 __wrapper func은 return한다. 102 | ``` 103 | 104 | ```python 105 | @decorator_func 106 | def add(a,b): 107 | '''To add a and b 108 | 109 | Args: 110 | a : int or numeric type 111 | b : int or numeric type 112 | Returns: 113 | a + b : int or numeric type 114 | ''' 115 | return a + b 116 | 117 | result = add(1,3) 118 | print(result) 119 | print(add.__name__) 120 | print(add.__doc__) 121 | ``` 122 | 123 | ```python 124 | things to do 125 | 4 126 | add 127 | To add a and b 128 | 129 | Args: 130 | a : int or numeric type 131 | b : int or numeric type 132 | Returns: 133 | a + b : int or numeric type 134 | ``` 135 | 136 | ### Example : time_check 137 | function마다 실행되는 데 걸리는 시간을 측정하는 기능을 추가하기위해, decorator function으로서의 `time_check` function을 구현하고, decorator keyword인 `@`를 활용하는 예제 138 | 139 | ```python 140 | import time 141 | from functools import wraps 142 | 143 | def time_check(func): 144 | @wraps(func) 145 | def __wrapper(*args, **kwargs): 146 | start = time.time() 147 | result = func(*args, **kwargs) 148 | elapsed = time.time() - start 149 | print('{} execution time : {} sec'.format(func.__name__, round(elapsed, 6))) 150 | return result 151 | return __wrapper 152 | ``` 153 | 154 | ```python 155 | @time_check 156 | def add(a,b): 157 | '''To add a and b 158 | 159 | Args: 160 | a : int or numeric type 161 | b : int or numeric type 162 | Returns: 163 | a + b : int or numeric type 164 | ''' 165 | return a + b 166 | ``` 167 | 168 | ```python 169 | result = add(1,3) 170 | print(result) 171 | print(add.__name__) 172 | print(add.__doc__) 173 | ``` 174 | 175 | ```python 176 | add execution time : 1e-06 sec 177 | 4 178 | add 179 | To add a and b 180 | 181 | Args: 182 | a : int or numeric type 183 | b : int or numeric type 184 | Returns: 185 | a + b : int or numeric type 186 | ``` -------------------------------------------------------------------------------- /Python/How to use functional programming.md: -------------------------------------------------------------------------------- 1 | # How to use functional programming 2 | 3 | Python에서 **Functional programming**을 활용하는 방법으로 `lambda`를 활용하며, `lambda`로 선언한 **Anonymous function**은 주로 **Functional programming**을 더 효율적으로 활용하기위해서 `map`, `filter`, `reduce` 등과 같이 활용되며, 이 때 **Functional programming**이 지원하는 **Lazy evaulation**은 `map`, `filter`에 적용된다 4 | 5 | * Reference 6 | + 7 | + 8 | + 9 | + 10 | + 11 | 12 | ### lambda 13 | Python에서 `lambda`는 **Anonymous function**을 선언하기 위한 것으로 **Anonymous function**을 활용하는 이유는 한번만 사용할 function이라서, **global frame**에 function에 대한 메모리를 할당할 필요가 없을 경우에 활용한다. 14 | 15 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=result%20%3D%20%28lambda%20a,%20b%20%3A%20a%20%2B%20b%29%281,3%29%0Aprint%28result%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 16 | 17 | ```python 18 | result = (lambda a, b : a + b)(1,3) 19 | print(result) 20 | ``` 21 | 22 | ```python 23 | 4 24 | ``` 25 | 26 | 물론 function을 명시적으로 선언하여, **global variable**에 해당 function을 variable이 가리키게 할 수도 있다. 즉 **global frame**에 function에 대한 메모리를 할당할 수 도 있다. 27 | 28 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=f%20%3D%20lambda%20a,%20b%20%3A%20a%20%2B%20b%0Aresult%20%3D%20f%281,%203%29%0Aprint%28result%29&cumulative=false&curInstr=6&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 29 | 30 | ```python 31 | f = lambda a, b : a + b 32 | result = f(1, 3) 33 | print(result) 34 | ``` 35 | 36 | ```python 37 | 4 38 | ``` 39 | 40 | ### Lazy evaluation 41 | **Lazy evaluation**은 **Functional programming**의 특징 중 하나로 필요할 때만 연산을 수행하여, 실행 속도를 계산하는 방법이다. Python에서 `iterator` object에 대해서만 동작하며, `iterator` object는 간단하게 말하자면 `next` function으로 데이터를 순차적으로 호출 가능한 object이다. (`__next__` method가 존재한다는 뜻) Python에서는 `map`, `filter`가 생성하는 각각의 `map` object, `filter` object 등이 `iterator` object이다. `reduce`로 하는 연산은 **Lazy evaluation**이 아니다. 그러므로 주의해야할 것은 `map` object, `filter` object를 일부러 `list` function을 이용하여, `iterable`로 바꾸는 것은 멍청하고 비효율적인 행동이다. **Lazy evaluation**을 활용하지 못하기 때문이다. `lambda`와는 아래와 같이 조합하여 활용하며, `iterable` object와 `iterator` object의 차이는 아래와 같다. 42 | 43 | * `iterable` object 44 | + `__iter__` method가 정의되어있는 sequential data type의 object 45 | + `iter` function의 해당 `iterable` object를 넣으면, object의 element를 모두 가지고 있는 `iterator` object를 생성함 (eg. `list_iterator`, `str_iterator`, `tuple_iterator`) 46 | + `for` loop으로 순회가능하며, `iterable` object를 `for` loop로 순회하면 내부적으로 해당 `iterable` object를 기반으로 `iterator` object를 생성하여 `next` function을 적용하는 것과 같음 47 | 48 | * `iterator` object 49 | + `__next__` method와 `__iter__` method가 정의되어있는 sequential data type의 object 50 | + 해당 object를 `for` loop로 한번 순회하면 그 이후 순회해도 object의 element에 접근불가 51 | + `next` function으로 object의 element를 다 꺼내면 `StopIteration` error 발생 52 | 53 | #### map 54 | `map`은 function과 `iterable`한 object를 받아서 `iterator` object를 내준다. 55 | 56 | ```python 57 | some_list = [1, 2, 3, 4, 5] 58 | result = map(lambda elm : elm + 1, some_list) 59 | print(result) 60 | ``` 61 | 62 | ```python 63 | 64 | ``` 65 | 66 | `map` object가 `iterator` object인지 아래와 같이 확인, `next` function이 동작한다. 67 | 68 | ```python 69 | try: 70 | while True: 71 | print(next(result)) 72 | except StopIteration as e: 73 | print('iterator가 다 element를 내주었습니다.') 74 | ``` 75 | 76 | ```python 77 | 2 78 | 3 79 | 4 80 | 5 81 | 6 82 | iterator가 다 element를 내주었습니다. 83 | ``` 84 | 85 | 물론 `for` 문도 동작한다. 86 | 87 | ```python 88 | for elm in result: 89 | print(elm) 90 | ``` 91 | 92 | ```python 93 | 2 94 | 3 95 | 4 96 | 5 97 | 6 98 | ``` 99 | 100 | #### filter 101 | `filter`는 function과 `iterable`한 object를 받아서 `iterator` object를 내준다. 이 때 function의 return 값은 `True` 또는 `False`이도록 하며, `filter`는 `iterable` object에서 `True`인 것만 뽑아 `iterator` object를 만든다. 102 | 103 | ```python 104 | # boolean으로 봤을 때, True인 것과 False인 것 골라내기 105 | some_list = [{},[],0,'',None,{'userid' : 'aisolab'}, 106 | ['modulab'], 10, 'boseop'] 107 | 108 | true_list = filter(lambda elm : bool(elm) == True, some_list) 109 | false_list = filter(lambda elm : bool(elm) == False, some_list) 110 | print(true_list, false_list) 111 | ``` 112 | 113 | ```python 114 | 115 | ``` 116 | 117 | `filter` object가 `iterator` object인지 아래와 같이 확인, `next` function이 동작한다. 118 | 119 | ```python 120 | try: 121 | while True: 122 | print(next(true_list)) 123 | except StopIteration as e: 124 | print('iterator가 다 element를 내주었습니다.') 125 | ``` 126 | 127 | ```python 128 | {'userid': 'aisolab'} 129 | ['modulab'] 130 | 10 131 | boseop 132 | iterator가 다 element를 내주었습니다. 133 | ``` 134 | 135 | 물론 `for` 문도 동작한다. 136 | 137 | ```python 138 | for elm in false_list: 139 | print(elm) 140 | ``` 141 | 142 | ```python 143 | {} 144 | [] 145 | 0 146 | 147 | None 148 | ``` 149 | 150 | ### reduce 151 | `reduce` function은 function과 `iterable`한 object를 받아 하나의 value를 내준다. 152 | 153 | ```python 154 | from functools import reduce 155 | 156 | some_list = [1,2,3,4,5] 157 | add_result = reduce(lambda elm1, elm2 : elm1 + elm2, some_list) 158 | max_result = reduce(lambda elm1, elm2 : elm1 if elm1 >= elm2 else elm2, some_list) 159 | print(add_result, max_result) 160 | ``` 161 | 162 | ```python 163 | 15 5 164 | ``` 165 | 166 | 또한 `reduce` function은 initial value를 줄 수 있다. 167 | 168 | ```python 169 | add_initial_result = reduce(lambda elm1, elm2 : elm1 + elm2, some_list, 10) 170 | print(add_initial_result) 171 | ``` 172 | 173 | ```python 174 | 25 175 | ``` 176 | 177 | initail value를 이용하여 아래와 같이 빈도수를 세는 reduce 연산도 가능하다. 178 | ```python 179 | result = reduce(lambda dic, elm : dic.update({elm : dic.get(elm, 0) + 1}) or dic, char_list, {}) 180 | print(result) 181 | ``` 182 | 183 | ```python 184 | {'a': 3, 'b': 2, 'c': 1} 185 | ``` 186 | -------------------------------------------------------------------------------- /Python/How to use closure.md: -------------------------------------------------------------------------------- 1 | # How to use closure 2 | 3 | Python에서 **객체지향(Object-Oriented Programming, OOP)** 을 구현하는 수단 중 하나인 **closure**를 사용하는 방법에 대하여 정리, 사실 Python은 을 `class`로 지원하기 때문에, ***사실 closure를 사용할 일은 거의 없으며***, 먼저 **closure**에 대해 정리하기에 **namespace**을 알아야 하기 때문에 같이 정리 4 | 5 | ### namespace 6 | 7 | variable에는 **global variable**과 **local variable**이 있으며 **local variable**의 경우는 **global variable**과 달리 function을 선언할 때, 해당 function의 **namespace**에서 존재한다. 즉 다시 말해서 **local variable**은 각각의 function마다 각각의 function **namespace**에 존재한다. 요약해서 정리하면 아래와 같다. 8 | 9 | * global variable : variable, function (Python에서는 모든 것이 object이므로) 10 | * local variable : function 내에서 선언되는 variable들 11 | 12 | 코드로는 아래와 같이 확인해볼 수 있다. 13 | 14 | #### global variable vs local variable 15 | ```python 16 | a = 10 # global variable 17 | def func(): 18 | a = 20 # func namespace에 있는 local variable a 19 | print('{} in func'.format(a)) 20 | 21 | func() 22 | print(a) 23 | ``` 24 | 25 | ```bash 26 | 20 in func # func namespace에 있는 local variable a 출력 27 | 10 # global variable a 출력 28 | ``` 29 | 30 | #### function 내부에서 global variable 참조 또는 수정 31 | **local variable**을 생성하지않고, function의 내부에서 **global variable**을 참조하는 것이 가능하다. 단 이 경우에는 수정이 불가능하며 function 내부에서 **global variable**을 수정하고 싶으면 `global` keyword를 활용한다. ***그런데 사실 function 내부에서 global variable을 참조하거나 활용하는 것은 사실하지 말아야할 행위이다.*** 32 | 33 | ##### function 내부에서 global variable 참조 34 | ```python 35 | # function 내부에서 global variable을 참조하는 것이 가능 36 | a = 10 # global variable 37 | def func(): 38 | print(a) 39 | 40 | func() 41 | ``` 42 | 43 | ```bash 44 | 10 # global variable 45 | ``` 46 | 47 | ##### function 내부에서 global variable 수정 48 | * `global` keyword를 활용하지 않을 때, `UnboundLocalError` 발생 49 | ```python 50 | # function 내부에서 global variable을 global keyword없이 수정하는 것은 불가능 51 | a = 10 52 | def func(): 53 | a += 1 54 | print(a) 55 | 56 | func() 57 | ``` 58 | 59 | ```bash 60 | # UnboundLocalError 발생 61 | UnboundLocalError Traceback (most recent call last) 62 | in () 63 | 4 print(a) 64 | 5 65 | ----> 6 func() 66 | 67 | in func() 68 | 1 a = 10 69 | 2 def func(): 70 | ----> 3 a += 1 71 | 4 print(a) 72 | 5 73 | 74 | UnboundLocalError: local variable 'a' referenced before assignment 75 | ``` 76 | 77 | * `global` keyword를 활용하면, function 내부에서 **global variable** 수정가능 78 | 79 | ```python 80 | # 전역변수를 함수내에서 수정하려면 81 | a = 10 82 | def func(): 83 | global a 84 | a += 1 85 | print(a) 86 | 87 | func() 88 | print(a) 89 | ``` 90 | 91 | ```bash 92 | 11 # global variable 93 | 11 # global variable 94 | ``` 95 | 96 | #### nonlocal variable 97 | **nonlocal variable**은 ***global variable도 아니고 local variable도 아닌 variable이다.*** 사실상 Python에서 **closure**를 구현하기위한 핵심 내용 중 하나이다. 코드로는 아래와 같이 확인할 수 있다. 98 | 99 | ```python 100 | # function마다 고유한 namespace를 가지고 있다. 101 | a = 10 # global variable a 102 | def outer(): 103 | b = 20 # nonlocal variable: outer function의 namespace에 있는 variable b 104 | def inner(): 105 | c = 30 # local variable: inner funciont의 namespace에 있는 variable c 106 | print(a, b, c) 107 | inner() 108 | 109 | outer() 110 | ``` 111 | 112 | ```bash 113 | 10 20 30 114 | ``` 115 | 116 | `nonlocal` keyword를 이용해 **nonlocal variable**을 수정할 수 있다. 117 | 118 | ```python 119 | # function마다 고유한 namespace를 가지고 있다. 120 | a = 10 # global variable a 121 | def outer(): 122 | b = 20 # nonlocal variable: outer function의 namespace에 있는 variable b 123 | def inner(): 124 | nonlocal b 125 | c = 30 # local variable: inner funciont의 namespace에 있는 variable c 126 | b += 30 # nonlocal variable b를 수정함 127 | print(a, b, c) 128 | inner() 129 | 130 | outer() 131 | ``` 132 | 133 | ```bash 134 | 10 50 30 135 | ``` 136 | 137 | ### closure 138 | **closure**는 **nonlocal variable**을 이용하여 구현하며, **nonlocal variable**을 상태 정보(free variable)를 저장하는데 활용한다. 139 | 140 | #### Template 141 | **closure**를 구현하는 방식은 크게 아래의 형태를 벗어나지 않는다. 142 | 143 | ```python 144 | # 외부함수와 내부함수가 있고 145 | # 내부함수를 return한다. 146 | def outer(a): 147 | # nonlocal variable인 a를 outer function에 argument로 선언하고 parameter를 148 | # 전달받는 형태로 보통 활용한다. 149 | # nonlocal variable a, b를 상태를 저장하는 free variable로 활용한다. 150 | 151 | b = 10 152 | def inner(c, d): 153 | nonlocal b 154 | b += 1 155 | return a + b + c + d 156 | return inner 157 | ``` 158 | 159 | ```python 160 | add = outer(a = 1) 161 | result = add(1,2) 162 | 163 | print(result) 164 | ``` 165 | 166 | ```bash 167 | 15 168 | ``` 169 | 170 | #### Example 171 | ##### Bank account를 closure로 구현 172 | ```python 173 | # 은행계좌를 closure로 구현 174 | def account(name, money): 175 | def change_money(amount): 176 | nonlocal money 177 | money += amount 178 | return name, money 179 | return change_money 180 | 181 | aisolab_acnt = account('aisolab', 300) 182 | modulab_acnt = account('modulab', 5000) 183 | 184 | print(aisolab_acnt(200), modulab_acnt(5000)) 185 | print(aisolab_acnt.__name__, modulab_acnt.__name__) 186 | print(aisolab_acnt.__class__, modulab_acnt.__class__) 187 | print(aisolab_acnt.__code__ == modulab_acnt.__code__) 188 | print(aisolab_acnt == modulab_acnt) 189 | ``` 190 | 191 | ```bash 192 | ('aisolab', 500) ('modulab', 10000) 193 | change_money change_money 194 | 195 | True 196 | False 197 | ``` 198 | 199 | ##### Digging the inside of closure 200 | * function 내부의 closure에 **nonlocal variable** (상태정보를 담고있는 free variable을 담는데 활용)의 값들을 저장 201 | 202 | ```python 203 | aisolab_closure = aisolab_acnt.__closure__ 204 | print([cell.cell_contents for cell in aisolab_closure]) 205 | ``` 206 | 207 | ```bash 208 | [500, 'aisolab'] 209 | ``` 210 | 211 | * function의 code object에 **nonlocal variable** (상태정보를 담고있는 free variable을 담는데 활용) name과 **local variable**의 name을 저장 212 | 213 | ```python 214 | aisolab_code_object = aisolab_acnt.__code__ 215 | print(aisolab_code_object.co_freevars) 216 | print(aisolab_code_object.co_varnames) 217 | ``` 218 | 219 | ```bash 220 | ('money', 'name') 221 | ('amount',) 222 | ``` 223 | -------------------------------------------------------------------------------- /CS/Integer.md: -------------------------------------------------------------------------------- 1 | # Integer 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | * Reference 5 | + 6 | 7 | ## 1. 컴퓨터에서 수를 표현하는 방법 8 | 수를 표현하는 방법을 **기수법(numeral system)** 이라고 하며, **밑수(base)** 를 정하면 밑수 개수만큼의 **숫자(digit)** 를 사용해 수를 나타낼 수 있다. 9 | 10 | ### 1.1 10진수 11 | 10진수는 수를 표현하는 데 숫자를 총 열 개 사용한다. 밑수는 10이고 0부터 9까지의 총 열 개의 숫자로 모든 수를 표현한다. 12 | 13 | $$ 14 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 15 | $$ 16 | 17 | ### 1.2 2진수 18 | 2진수는 수를 표현하는 데 숫자 0과 1만 사용한다. 컴퓨터가 인식할 수 있는 표현방법이다. 19 | 20 | $$ 21 | 0, 1 22 | $$ 23 | 24 | ### 1.3 16진수 25 | 16진수는 수를 표현하는 데 숫자를 총 열여섯 개 사용한다. 아래와 같다. 보통 많은 자리의 2진수 숫자를 4개씩 짤라서 16진수로 표현한다. 26 | 27 | $$ 28 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f 29 | $$ 30 | 31 | 16진수 $a, b ,c ,d ,e ,f$ 를 2진수로 바꿔보면 아래와 같다. 32 | 33 | $$ 34 | a = 1010_{(2)}, b = 1011_{(2)}, c = 1100_{(2)}, d = 1101_{(2)}, e = 1110_{(2)}, f = 1111_{(2)} 35 | $$ 36 | 37 | ## 2. 10진수를 2진수로 38 | 39 | 10진수를 2진수로 바꿀 때는 다음과 같이 바꾼다. 예를 들어 25를 2진수로 바꿔보자. 40 | 41 | 1. 25를 2의 거듭제곱의 합으로 쪼갠다 42 | 43 | $25 \rightarrow 16+9$ 44 | 45 | $25 \rightarrow 16+8+1=2^{4}+2^{3}+2^{0}$ 46 | 47 | 2. $2^{2}$ 나 $2^{1}$ 처럼 중간에 빠진 지수는 0을 이용해 표현한다. 48 | 49 | $$ 50 | 25 \rightarrow 1 \times 2^{4} + 1 \times 2^{3} + 0 \times 2^{2} + 0 \times 2^{1} + 1 \times 2^{0} 51 | $$ 52 | 53 | 3. 2의 거듭제곱 수를 제외하고 앞의 수 1과 0만 모아 나열한다. 54 | 55 | $$ 56 | 11001_{(2)} 57 | $$ 58 | 59 | Python으로 확인해보면 아래와 같다. 60 | 61 | ```python 62 | a = 25 63 | print(bin(a)) 64 | ``` 65 | 66 | ```bash 67 | 0b11001 # 맨앞의 0b는 2진수를 의미하는 binary 68 | ``` 69 | 70 | ## 3. 2진수를 10진수로 71 | 2진수를 10진수로 바꿀 때는 아래와 같이 바꾼다. 72 | 73 | $$ 74 | 11001_{(2)} \rightarrow 1 \times 2^{4} + 1 \times 2^{3} + 0 \times 2^{2} + 0 \times 2^{1} + 1 \times 2^{0} = 25 75 | $$ 76 | 77 | ## 4. 16진수를 2진수로 78 | 16진수 한 자리는 0부터 f까지 최대 10진수 15까지 표현할 수 있다. 16진수 f는 10진수로는 15이며 이를 2진수로 변환하면 $1111_{(2)}$ 이다. ***즉 16진수 한 자리는 2진수 네 자릿수까지 표현할 수 있다.*** 16진수는 즉 4비트를 이용하여 표현한다. 예를 들어 16진수 3을 2진수로 바꿔보자. 79 | 80 | 1. 16진수 3은 10진수 3과 같으므로, 10진수에서 2진수로 바꾸는 방법을 활용한다. 81 | 82 | $3_{(16)}= 3 \rightarrow 2 + 1$ 83 | 84 | $3_{(16)}= 3 \rightarrow 2 + 1 = 1 \times 2^{1} + 1 \times 2^{0}$ 85 | 86 | 2. 16진수 한자리는 2진수 네 자릿수까지 표현할 수 있으므로, 4비트로 표현한다. 빠진 지수는 0을 이용해 표현한다. 87 | 88 | $$ 89 | 3 \rightarrow 0 \times 2^{3} + 0 \times 2^{2} + 1 \times 2^{1} + 1 \times 2^{0} 90 | $$ 91 | 92 | 3. 2의 거듭제곱 수를 제외하고 앞의 수 1과 0만 모아 나열한다. 93 | 94 | $$ 95 | 0011_{(2)} 96 | $$ 97 | 98 | ## 5. 코딩으로 확인하는 진수 변환 99 | ```python 100 | origin = [0x0, 0x1, 0x2, 0x3, 0x4, 101 | 0x5, 0x6, 0x7, 0x8, 0x9, 102 | 0xa, 0xb, 0xc, 0xd, 0xe, 0xf] 103 | trans = list(map(lambda elm : bin(elm), origin)) 104 | 105 | for idx in range(len(origin)): 106 | print('16진수 : {} ---> 2진수 : {}'.format(hex(origin[idx]), trans[idx])) 107 | ``` 108 | 109 | ```bash 110 | 16진수 : 0x0 --> 2진수 : 0b0 #0b0000 111 | 16진수 : 0x1 --> 2진수 : 0b1 #0b0001 112 | 16진수 : 0x2 --> 2진수 : 0b10 #0b0010 113 | 16진수 : 0x3 --> 2진수 : 0b11 #0b0011 114 | 16진수 : 0x4 --> 2진수 : 0b100 #0b0100 115 | 16진수 : 0x5 --> 2진수 : 0b101 #0b0101 116 | 16진수 : 0x6 --> 2진수 : 0b110 #0b0110 117 | 16진수 : 0x7 --> 2진수 : 0b111 #0b0111 118 | 16진수 : 0x8 --> 2진수 : 0b1000 119 | 16진수 : 0x9 --> 2진수 : 0b1001 120 | 16진수 : 0xa --> 2진수 : 0b1010 121 | 16진수 : 0xb --> 2진수 : 0b1011 122 | 16진수 : 0xc --> 2진수 : 0b1100 123 | 16진수 : 0xd --> 2진수 : 0b1101 124 | 16진수 : 0xe --> 2진수 : 0b1110 125 | 16진수 : 0xf --> 2진수 : 0b1111 126 | ``` 127 | 128 | 예를 들어, 8비트 컴퓨터의 메모리 주소가 2진수 $0010 1101_{(2)}$ 라면 2진수 네 자릿수를 16진수 한 자릿수로 표현할 수 있으므로, $2d_{(16)}$ 으로 나타낼 수 있다. 보통 메모리 주소를 나타낼 때는 16진수를 활용한다. 129 | 130 | ```python 131 | address_8bit = 0b00101101 132 | print(hex(address_8bit)) 133 | ``` 134 | 135 | ```bash 136 | 0x2d 137 | ``` 138 | 139 | 32비트 컴퓨터의 메모리 주소는 서른두 자릿수의 2진수 수가아닌 여덟 자릿수의 16진수 수를 이용하여 표현한다. 140 | 141 | ```python 142 | address_32bit = 0x1234abcd 143 | print(bin(address_32bit)) 144 | ``` 145 | 146 | ```python 147 | 0b10010001101001010101111001101 148 | # 0b1/0010/0011/0100/1010/1011/1100/1101 149 | # Python에서는 0으로 구지 채우지 않아서 표현하므로 4자릿수로 오른쪽에서 왼쪽으로 짤라 나갔을 때, 마지막에 150 | # 남는 1은 0001로 봐야한다. 151 | ``` 152 | 153 | ## 6. 양의 정수 154 | 컴퓨터는 **정수(integer)** 를 1바이트, 2바이트, 4바이트, 8바이트 등 다양한 크기로 저장한다. ***예를 들어, 메모리의 1바이트에 정수 25를 저장할 경우 아래와 같다.*** 155 | 156 | * 부호를 나타내는 데 1비트를 활용한다. 157 | + 맨 앞의 비트가 0이면 양수, 1이면 음수 158 | * 10진수 25를 2진수로 $11001_{(2)}$ 변환, 빈곳으로 0으로 채운다. 159 | * 결론적으로 메모리에 아래와 같이 저장한다. 160 | 161 | $$ 162 | 0001 \ 1001_{(2)} 163 | $$ 164 | 165 | 메모리에 1바이트로 정수를 저장할 경우, 8비트이므로 256개의 정수를 표현하여 할 수 있으며, -128 ~ 127의 정수를 저장할 수 있다. 음의 정수를 메모리에 저장할 때는, 양의 정수와 방식이 많이 달라진다. 166 | 167 | ## 7. 음의 정수 168 | 컴퓨터는 음수를 **보수(complement)** 형태로 저장한다. 169 | 170 | ### 7.1 보수의 개념 171 | **보수(complement)** 는 쉽게 말해 보충해주는 수로 예를 들면 아래와 같다. 172 | 173 | * 10진수에서 26의 9의 보수와 10의 보수를 구한다고 하면 174 | + $26 + 73 = 99$ 이므로 26의 9의 보수는 73 175 | + 26의 10의 보수는 $73 + 1 = 74$ 이다. 176 | 177 | ### 7.2 2의 보수 178 | 컴퓨터는 음수를 표현할 때, 2의 보수를 사용한다. 예를들어 2진수 $1010_{(2)}$ 의 2의 보수를 구하려면 아래와 같이 구한다. 179 | 180 | * 2진수에서 $1010_{(2)}$ 의 1의 보수를 먼저 구한다. 181 | + $1010_{(2)} + 0101_{(2)} = 1111_{(2)}$ 이므로 1의 보수는 $0101_{(2)}$ 182 | + 위 과정을 그냥 각 자리수에서 반전시킨다고 생각해도 된다. 183 | * 구한 1의 보수에 1 ($0001_{(2)}$)을 더한다. 184 | + $0101_{(2)} + 0001_{(2)} = 0110_{(2)}$ 이므로 2의 보수는 $0110_{(2)}$ 185 | 186 | ### 7.3 음수의 표현 187 | 188 | 1바이트로 정수로 표현할 경우, 음의 정수인 -4는 아래와 같이 표현된다. 189 | 190 | * 먼저 양의 정수 4를 2진수로 $0000 \ 0100_{(2)}$ 로 표현한다. 191 | * 2진수에서의 1의 보수를 구한다. 192 | + $0000 \ 0100_{(2)} + 1111 \ 1011_{(2)} = 1111 \ 1111_{(2)}$ 이므로 1의 보수는 $1111 \ 1011_{(2)}$ 193 | * 구한 1의 보수에 1 ($0000 \ 0001_{(2)}$)을 더한다. 194 | + $1111 \ 1011_{(2)} + 0000 \ 0001_{(2)} = 1111 \ 1100_{(2)}$ 이므로 2의 보수는 $1111 \ 1100_{(2)}$ 195 | * -4를 $1111 \ 1100_{(2)}$ 표현하여 메모리 1바이트에 저장한다. 196 | 197 | 코드로 확인해보면 아래와 같다. 198 | ```python 199 | a = -4 200 | print(a.to_bytes(1, byteorder = 'little', signed = True)) 201 | ``` 202 | 203 | ```bash 204 | b'\xfc' 205 | ``` 206 | 207 | ```python 208 | print(bin(0xfc)) 209 | ``` 210 | 211 | ```bash 212 | 0b11111100 213 | ``` 214 | 215 | ### 7.4 2의 보수로 표현하는 이유 216 | 컴퓨터에서 음의 정수를 2의 보수로 표현하는 이유는 아래와 같다. 메모리 1바이트에 정수를 저장하는 경우로 생각해보면 217 | 218 | * 양의 정수와 음의 정수를 모두 양의 정수를 저장하는 방식으로 저장한다고 하면, 0을 표현하는 방식이 $0000 \ 0000_{(2)}$, $1000 \ 0000{(2)}$ 두 가지가 된다. 이는 아래의 문제를 발생시킨다. 219 | + 컴퓨터 입장에서 수 하나를 더 표현할 수 있는 데, 비트 하나를 낭비하게됨 220 | + 두 수를 비교할 때 CPU에서 뺄셈을 하는데 +0과 -0을 비교하면 결과 값이 예상과 다르게 나옴 221 | * 정수의 뺄셈연산이 다음과 같이 되어 컴퓨터 입장에서 깔끔함, $9 - 4 \rightarrow 9 + (-4)$ 를 함 222 | + $0000 \ 1001_{(2)} + 1111 \ 1100_{(2)} = 1 \ 0000 \ 0101_{(2)}$ 223 | + 위 처럼 계산하게되며, 계션 결과를 보면 **받아올림(carring)** 이 발생하나, 메모리 1바이트에 정수를 표현하고 있으므로, 가장 왼쪽의 1을 버리면됨. 즉 잘 계산이 되었음. 224 | 225 | -------------------------------------------------------------------------------- /Clean_code/CC01_Introduction, Tools and Formatting.md: -------------------------------------------------------------------------------- 1 | # Clean code in Python 2 | 본 내용은 **파이썬 클린코드 (유지보수가 쉬운 파이썬 코드를 만드는 비결)** 를 읽고 간략하게 내용을 정리한 것입니다. 3 | * link : https://github.com/rmariano/Clean-code-in-Python/tree/master/book/src/ch01 4 | ## Chapter01 소개, 코드 포매팅과 도구 5 | 이 장에서 다루는 주제는 다음과 같다. 6 | * 클린 코드는 포매팅 이상의 훨씬 중요한 것을 의미한다. 7 | * 떄문에 표준 포매팅을 유지하는 것이 유지보수성의 핵심 유의사항이다. 8 | * 파이썬이 제공하는 기능을 사용하여 자체 문서화된 코드를 작성하는 방법 9 | * 코드의 레이아웃을 일정하게 유지하여 팀 멤버들이 문제의 본질을 해결하는 데 초점을 맞출 수 있도록 도구를 설정하는 방법 10 | 11 | ### 클린 코드의 의미 12 | 프로그래밍 언어라는 것은 인간의 아이디어를 컴퓨터에 전달하기위해 사용한다는 의미도 있지만 뿐만 더 중요한 의미는 ***아이디어를 다른 개발자에게 전달한다는 것***에 있다. 고로 클린 코드인 지 아닌 지는 ***다른 엔지니어가 코드를 읽고 유지 관리할 수 있는 지 여부에 달려있다.*** 그러므로 전문가인 우리 자신이 클린 코드를 판단할 수 있는 유일한 사람이다. 13 | 14 | ### 클린 코드의 중요성 15 | * 유지보수성 향상 16 | + 코드가 유지보수 가능한 상태로 가독성이 높으면, 민첩한 개발과 지속적인 배포가 가능하다. 17 | * 기술부채(나쁜 결정이나 적당한 타협의 결과로 생긴 소프트웨어적 결함)의 감소 18 | + 코드가 유지보수 가능한 상태로 가독성이 높으면, 코드를 수정하고 리팩토링하는 시간을 줄 일 수 있다. -> 기술 부채에 대한 비용이 싸진다. 19 | 20 | ### 클린 코드에서의 포매팅의 역할 21 | 클린 코드는 ***품질 좋은 소프트웨어를 개발하고, 견고하고 유지보수가 쉬운 시스템을 만들고, 기술 부채를 회피하는 것을 말한다.*** 즉 유지보수성이나 소프트웨어 품질에 관한 것을 의미하며, 올바르게 포매팅 하는 것을 이와 같은 작업을 효율적으로 하는 데에 있어 중요한 역할을 한다. 22 | 23 | ### 프로젝트 코딩 스타일 가이드 준수 24 | 코딩 가이드라인은 품질 표준을 지키기 위해 프로젝트에서 따라야만 하는 최소한의 요구사항이다. 좋은 코드레이 아웃에서 가장 필요한 특성은 일관성이다. 코드가 일관되게 구조화되어 있으면 가독성이 높아지고 이해하기 쉬워진다. 특히 ***파이썬이 따라야하는 코딩 스타일은 [PEP-8](https://www.python.org/dev/peps/pep-0008/)이며***, 작업하는 프로젝트의 특수성(예: 한 줄의 길이, 들여쓰기의 간격 등)을 확장하거나 일부만 채택할 수 있다. 다른 표준을 고민하기보다는 그대로 사용하기나 확장해서 사용할 것을 권장한다. 25 | 26 | ### Docstring과 Annotation 27 | 코드를 문서화하는 것은 주석을 추가하는 것과는 다르다. 주석(comment)는 가급적 피하는 것이 좋다. 문서화를 통해 데이터 타입이 무엇인 지 설명하고, 예제를 제공할 수 있다. 28 | 29 | #### Docstring 30 | 31 | ***docstring은 소스 코드에 포함된 주석(comment)가 아니라 문서(documentation)이다.*** 코드에 주석을 다는 것은 여러가지 이유로 나쁜 습관이며 대표적으로 아래와 같다. (드물긴하지만 외부 라이브러리에 오류가 있다면 짧은 주석을 다는 것은 괜찮다.) 32 | 33 | 1. 주석은 코드로 아이디어를 제대로 표현하지 못했음을 나타내는 것이다. 34 | 2. 오해의 소지가 있다. 35 | 36 | docstring은 주석을 다는 것이 아니라 코드의 특정 컴포넌트(모듈, 클래스, 메서드 또는 함수)에 대한 문서화이며, 이런 컴포넌트에 사용하는 것은 매우 권장되는 부분이다. ***가능한 많은 docstring을 추가하는 것이 좋다.*** docstring을 코드에 포함시키는 것이 좋은 이유는 ***파이썬이 동적 타이핑을 하기 때문이다.*** 예를 들어 함수는 파라미터의 값으로 무엇이든 사용할 수 있다. 파이썬은 파라미터의 타입을 체크하거나 강요하지 않는다. 따라서 함수를 수정해야 하는데 함수의 이름과 파라미터의 이름이 충분히 설명적이라면 굉장히 운이 좋은 것이다. 그렇다 해도 아직 정확히 어떤 타입을 사용해야 하는 지 알 수 없다. 이런 경우 docstring이 도움이 될 것이다. 예상되는 함수의 입력과 출력을 문서화하면 사용자가 사용할 때 함수가 어떻게 동작하는 지 이해하기 쉽다. 표준 라이브러리에 docstring을 사용하는 좋은 예가 있다. 37 | 38 | ```python 39 | Docstring: 40 | D.update([E, ]**F) -> None. Update D from dict/iterable E and F. 41 | If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] 42 | If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v 43 | In either case, this is followed by: for k in F: D[k] = F[k] 44 | Type: method_descriptor 45 | ``` 46 | 47 | docstring은 코드에서 분리되거나 독립된 것이 아니다. 코드의 일부가 되어 접근할 수 있어야한다. 객체에 docstring이 정의되어 있으면 `__doc__` 속성을 통해 접근이 가능하다. 48 | 49 | ```python 50 | print(dict.__doc__) 51 | ``` 52 | ```python 53 | dict() -> new empty dictionary 54 | dict(mapping) -> new dictionary initialized from a mapping object's 55 | (key, value) pairs 56 | dict(iterable) -> new dictionary initialized as if via: 57 | d = {} 58 | for k, v in iterable: 59 | d[k] = v 60 | dict(**kwargs) -> new dictionary initialized with the name=value pairs 61 | in the keyword argument list. For example: dict(one=1, two=2) 62 | ``` 63 | 64 | #### Annotation 65 | 66 | ***[PEP-3107]()에는 annotation을 소개하고 있다. 기본 아이디어는 코드 사용자에게 함수 인자로 어떤 값이 와야 하는지 힌트를 주자는 것이다. 정말 힌트를 주는 것이며, 어노테이션은 타입 힌팅(type hinting)을 활성화한다.*** annotation을 사용해 변수의 예상 타입을 지정할 수 있다. 실제로는 타입 뿐 아니라 변수를 이해하는 데 도움이 되는 어떤 형태의 메타데이터라도 지정할 수 있다. 아래의 예제를 살펴보자. 67 | 68 | ```python 69 | class Point: 70 | def __init__(self, lat, long): 71 | self.lat = lat 72 | self.long = long 73 | 74 | def locate(latitude: float, longitude: float) -> Point: 75 | """맵에서 좌표에 해당하는 객체를 검색""" 76 | ``` 77 | 78 | 여기서 `latitude`와 `longitude`는 `float` 타입의 변수이다. 이 것을 통해 함수 사용자는 예상되는 타입을 알 수 있다. 하지만 파이썬이 타입을 검사하거나 강제하지는 않는다. 또한 함수 반환 값에 대한 예상 타입을 지정할 수도 있다. 위 예제에서 `Point`는 사용자 정의 클래스이므로 반환되는 값이 `Point`의 인스턴스라는 것을 의미한다. 그러나 annotation으로 타입만 지정할 수 있는 것은 아니다. 파이썬 인터프리터에서 유효한 어떤 것도 사용할 수 있다. 예를 들어 변수의 의도를 설명하는 문자열, 콜백이나 유효소어 검사 함수로 사용할 수 있는 `callable`등이 있다. annotation을 사용하면 `__annotations__`라는 특수한 속성이 생긴다. 이 속성은 annotation의 이름과 값을 매핑한 사전 타입의 값이다. 앞의 예제에서는 다음과 같이 출력된다. 79 | 80 | ```python 81 | print(locate.__annotations__) 82 | ``` 83 | ```python 84 | {'latitude': , 'longitude': , 'return': } 85 | ``` 86 | 87 | ***[PEP-484](https://www.python.org/dev/peps/pep-0484/)를 적용하면 annotation을 통해 코드를 확인할 수 있다. 이 PEP는 타입 힌팅의 기본 원리를 정의한 것으로 annotation을 통해 함수의 타입을 체크할 수 있다. 타입 힌팅은 인터프리터와 독립된 추가 도구를 사용하여 코드 전체에 올바른 타입이 사용되었는 지 확인하고 호환되지 않는 타입이 발견되었을 때 사용자에게 힌트를 주는 것이다.*** 타입 힌팅은 코드의 타입을 확인하기 위한 도구 이상의 것을 의미한다. 파이썬 3.5부터는 새로운 `typing` 모듈이 소개되었고 파이썬 코드에서 타입과 annotation을 정의하는 방법이 크게 향상 되었다. 기본 개념은 코드의 시맨틱이 보다 의미 있는 개념을 갖게 되면 코드를 이해하기 쉽고 특정 시점에 어떻게 될 지 예측할 수 있다는 것이다. 파이썬 3.6부터는 함수 파라미터와 리턴 타입뿐만 아니라 변수에 직접 타입 힌팅을 할 수 있다. 이것은 [PEP-526]()에서 소개 되었으며, 다음과 같이 값을 지정하지 않은 채로 변수의 타입을 선언할 수 있다. 88 | 89 | ```python 90 | class Point: 91 | lat: float 92 | long: float 93 | 94 | print(Point.__annotations__) 95 | ``` 96 | ```python 97 | {'lat': , 'long': } 98 | ``` 99 | 100 | ### Annotation은 Docstring을 대체하는 것일까? 101 | docstring에 포함된 정보의 일부(eg. 함수의 파라미터 또는 속성의 타입을 문서화할 때 docstring을 사용하는 경우)는 annotation으로 이동시킬 수 있는 것이 사실이다. 그러나 ***docstring을 통해 보다 나은 문서화를 위한 여지를 남겨두어야 한다. 특히 동적 데이터 타입과 중첩 데이터 타입의 경우 예상 데이터의 예제를 제공하여 어떤 형태의 데이터를 다루는 지 제공하는 것이 좋다.*** 아래의 예제를 통해 확인해보자. 102 | 103 | ```python 104 | def data_from_response(response: dict) -> dict: 105 | """response에 문제가 없다면 reponse의 payload를 반환 106 | 107 | - response 사전의 예제:: 108 | { 109 | 'status': 200, # 110 | 'timestamp': '...', # 현재 시간의 ISO 포맷 문자열 111 | 'payload': {...} # 반환하려는 사전 데이터 112 | } 113 | 114 | - 반환 사전 값의 예제:: 115 | {'data': {...}} 116 | 117 | - 발생 가능한 예외: 118 | - HTTP status가 200이 아닌 경우 ValueError 발생 119 | """ 120 | if response['status'] != 200: 121 | raise ValueError 122 | return {'data': response['payload']} 123 | ``` 124 | 125 | ### 기본 품질 향상을 위한 도구 설정 126 | 127 | 동료가 작성한 코드를 살펴볼 때는 다음 질문을 해야 한다. 128 | 129 | * 이 코드를 동료 개발자가 쉽게 이해하고 따라갈 수 있을까? 130 | * 업무 도메인에 대해서 말하고 있는가? 131 | * 팀에 새로합류하는 사람도 쉽게 이해하고 효과적으로 작업할 수 있을까? 132 | 133 | 이전에 살벼보았듯이 코드 포매팅, 일관된 레이아웃, 적절한 들여쓰기를 검사하는 것만으로는 충분하지 않다. 더군다나 높은 수준의 엔지니어에게 이것은 당연한 것이므로 레이아웃의 개념을 뛰어넘는 그 이상의 것을 읽고 쓸 수 있어야한다. 따라서 이런 것들을 살펴보는 데 시간을 낭비하기보다는 실제 어떤 패턴이 사용되었는 지 살펴서 코드의 실제 의미와 가치를 이해하는 데 시간을 투자하는 것이 효과적이다. 이 모든 검사는 자동화해야한다. 테스트 또는 체크리스트의 일부가 되어 지속적인 통합 빌드(continuous integration build)의 하나가 되어야한다. 이러한 검사에 실패하면 빌드도 실패하야 한다. 이렇게 하는 것만이 코드 구조의 연속성을 확보할 수 있는 유일한 방법이다. 이것은 팀에서 참고할 수 있는 객관적인 지표 역할도 한다. ***일부 엔지니어 또는 팀 리더가 코드 리뷰 시 PEP-8에 대한 동일한 의견을 항상 말하도록 하게 만드는 대신, 빌드 시 자동으로 실패하도록 해야 객관성을 얻을 수 있다.*** 이를 위해 아래와 같은 것을 도입할 수 있다. 134 | 135 | * `Mypy`를 사용한 타입 힌팅 136 | * `Pylint`를 사용한 코드 검사 137 | * 자동 검사 설정 138 | * eg. 리눅스 개발환경에서 빌드를 자동화하기위해 Makefile을 사용, 빌드 외에도 포매팅 검사나 코딩 컨벤션 검사를 자동화하기 위해 사용할 수 있다. 139 | 140 | -------------------------------------------------------------------------------- /CS/Real number.md: -------------------------------------------------------------------------------- 1 | # Real number 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | ## 1. 실수 연산의 함정 5 | 왜 아래와 같은 현상이 발생하는 걸까? 6 | ```python 7 | a = .01 8 | result = .0 9 | 10 | for _ in range(100): 11 | result += a 12 | else: 13 | print(result) 14 | ``` 15 | 16 | ```bash 17 | a = .015625 18 | result = .0 19 | 20 | for _ in range(100): 21 | result += a 22 | else: 23 | print(result) 24 | ``` 25 | 26 | ```python 27 | 1.5625 28 | ``` 29 | 30 | ## 2. 부동소수점 31 | 컴퓨터는 **실수(real number)** 를 **부동소수점(floating-point)** 로 표현한다. 부동소수점이란 이름은 아래와 같이 실수를 표현할 수 있다는 거에 기인하여 이름이 붙었다. 32 | 33 | $$ 34 | 123.456 = 1.23456 \times 10^{2}, 12.3456 \times 10^{1}, 1234.56 \times 10^{-1}, 12345.6 \times 10^{-2} 35 | $$ 36 | 37 | ## 3. 단정도와 배정도 38 | * 단정도(single-precision) : 실수를 32비트(4바이트)로 표현하며 부호 1비트, 지수부 8비트, 가수부 23비트로 구성됨 39 | * 배정도(double-precision) : 실수를 64비트(8바이트)로 표현하며 부호 1비트, 지수부 11비트, 가수부 52비트로 구성, ***실수를 표현하는 데 사용하는 비트 수가 단정도의 두 배인 만큼 정밀도가 높으며, Python은 배정도를 사용한다.*** 40 | 41 | 이는 메모리에 저장하는 형식이다. 코드로도 확인할 수 있다. 42 | 43 | ```python 44 | import sys 45 | print(sys.float_info) 46 | ``` 47 | 48 | ```bash 49 | # dig : 배정도로 표현할 수 있는 10진수의 자리수 50 | # mant_dig : 가수의 비트 (정규화에 따른 1비트 + 가수의 man(가수부) 52비트) 51 | # epsilon이 중요 52 | sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1) 53 | ``` 54 | 55 | 배정도 부동소수점으로 표현할 수 있는 가장 큰 수, 가장 작은 수는 아래와 같다. 56 | 57 | ```python 58 | import sys 59 | print(sys.float_info.max, sys.float_info.min) 60 | ``` 61 | 62 | ```bash 63 | # 여기서 e는 지수를 뜻하는 exponent의 줄임말 64 | 1.7976931348623157e+308 2.2250738585072014e-308 65 | ``` 66 | 67 | ## 4. 1바이트 실수 자료형 설계하기 68 | 실수 표현방식은 아래와 같다. 69 | 70 | $$ 71 | \pm \ 1.man_{(2)} \times 2^{exp-bias} 72 | $$ 73 | 74 | $1.man$ 은 가수(mantissa/fraction)라고 하며 여기서 man은 가수부라고 한다. 2는 밑수(base), exp-bias는 지수(exponent)를 의미한다. ***위의 수식을 이용해, 7.75라는 10진 수 실수를 1바이트 부동소수점으로 표현하는 것은 아래의 과정을 거친다.*** 75 | 76 | ### 4.1 10진수 실수를 2진수 실수로 바꾸기 77 | 10진수 실수 7.75를 2진수 실수로 바꾸는 과정은 아래와 같다. 78 | 79 | 1. 10진수 실수 7.75를 2의 거듭제곱의 합으로 쪼갠다. 80 | 81 | $7.75 \rightarrow 4 + 2 + 1 + 0.5 + 0.25$ 82 | 83 | $7.75 \rightarrow 2^{2} + 2^{1} + 2^{0} + 2^{-1} + 2^{-2}$ 84 | 85 | 2. 지수를 1과 0을 이용해 표현한다. 86 | 87 | $7.75 \rightarrow 1 \times 2^{2} + 1 \times 2^{1} + 1 \times 2^{0} + 1 \times2^{-1} + 1 \times 2^{-2}$ 88 | 89 | 3. 2의 거듭제곱 수를 제외하고 앞의 수 1과 0만 모아 나열한다. 90 | 91 | $$ 92 | 111.11_{(2)} 93 | $$ 94 | 95 | ### 4.2 정규화 96 | **정규화(normalization)** 란 소수점 왼쪽에 위치한 가수 부분을 밑수보다 작은 자연수가 되도록 만드는 것이다. $111.11_{(2)}$ 를 정규화하려면 2진수의 밑수는 2이므로 2보다 작은 자연수는 1밖에 없으므로, 2진수의 경우 소수점 왼쪽의 가수부분은 항상 1이된다. 97 | 98 | $$ 99 | 111.11_{(2)} \rightarrow 1.1111_{(2)} \times 2^{2} 100 | $$ 101 | 102 | ### 4.3 메모리구조 103 | 정규화된 2진수 부동소수점 수 $1.1111_{(2)} \times 2^{2}$ 를 보면 가수에서 man 부분은 1111이고, exp-bias는 2이다. 1바이트의 메모리 구조를 아래와 같이 정하고 저장해보자. 104 | 105 | $$ 106 | 0 \ 0000 \ 000 \ [부호(sign) \ 지수부(exp) \ 가수부에서 \ man에 \ 해당되는 \ 부분] 107 | $$ 108 | 109 | * 첫 번째 비트 : 부호, 0이면 양수, 1이면 음수 110 | * 가운데 4비트 : 지수부, exp 값을 저장 111 | + 지수부에는 부호비트가 존재하지 않는다. 112 | + 0~15까지의 양수를 표현할 수 있다. 113 | + 음수지수도 필요하므로 bias를 7로 두고 지수부(exp)에서 bias를 뺀 값을 실제 지수로 사용 114 | + $bias = 2^{지수부의 비트수 - 1} - 1$ 115 | * 맨 뒤 3비트 : 가수부, man 값을 저장 116 | 117 | ***$1.1111_{(2)} \times 2^{2}$ 를 위에서 정한 1바이트의 메모리 구조에 저장하는 과정은 아래와 같다.*** 118 | 119 | * 부호비트는 0 120 | * $exp - bias = 2$ 이면서 $bias =7$ 이므로, $ exp = 9 $ 이며 exp를 2진수로 바꾸면 $1001_{(2)}$ 121 | * 가수부에서 man에 해당되는 부분은 3비트만 할당되어있는데, 실제 man에 해당하는 부분은 1111, 이 경우 뒷자리 1을 생략한다. 즉 $1.111_{(2)} \times 2^{2}$ 로 본다. 122 | 123 | 이 과정을 거치면 10진수 실수 7.75를 우리가 정한 1바이트 메모리 구조에 아래와 같이 저장할 수 있다. 124 | 125 | $$ 126 | 0 \ 1001 \ 111 \rightarrow 0100 \ 1111 127 | $$ 128 | 129 | ***10진수 실수 7.75를 위와 같이 1바이트 메모리 구조에 저장하고, 이를 16진수로 나타내면 0x4f이다.*** 130 | 131 | ### 4.4 1바이트 부동소수점의 표현 범위 132 | 133 | 위에서 설계한 1바이트 부동소수점으로 표현할 수 있는 가장 작은 수와 가장 큰 수는 아래와 같다. 134 | 135 | (단, 지수부 비트가 모두 0일 때($2^{-7}$), 지수부 비트가 모두 1일 때($2^{8}$)는 0.0, 정규화 불가능, 무한대, NaN(Not a Number) 등은 제외) 136 | 137 | * 표현할 수 있는 가장 작은 수 (지수부가 $0001$) 138 | + $1.000_{(2)} \times 2^{-6} = 0.015625$ 139 | * 표현할 수 있는 가장 큰 수 (지수부가 $1110​$) 140 | + $1.111_{(2)} \times 2^{7} = 240$ 141 | 142 | 우리가 설계한 1바이트 부동소수점으로 표현할 수 있는 10진수의 유효숫자는 이 경우 2가된다. 143 | 144 | ### 4.5 1바이트 부동소수점의 정밀도 145 | 146 | 10진수 실수 7.75를 우리가 설계한 1바이트 메모리구조에 담기위해서 2진수 실수로 바꾼 뒤, 부동소수점표현을 쓰고, 정규화를 하면 $1.111_{(2)} \times 2^{2}$ 가 된다. 이렇게 됬을 경우 우리는 1바이트 메모리구조에 실제로는 $1 \ 1001 \ 111 \rightarrow 1100 \ 1111$ 의 형태로 저장하는 것이고, 16진수로 표현하면 0x4f이다. ***위에서 설계한 1바이트 메모리구조는 7.75 실수를 완벽하게 표현하지는 못한다.*** 147 | 148 | $$ 149 | 1.111_{(2)} \times 2^{2} = 111.1_{(2)} = 1 \times 2^{2} \ +1 \times 2^{1} + 1 \times 2^{0} + 1 \times 2^{-1} = 7.5 150 | $$ 151 | 152 | ## 5. 정밀도에 대한 고찰 153 | ### 5.1 엡실론 154 | 실수 자료형에서 **엡실론(epsilon)** 이란 1.0과 그 다음으로 **표현 가능한 수(representable float)** 의 사이의 차이를 말한다. ***Python에서는 실수를 표현함에 있어서 배정도를 쓴다는 사실을 기억한 채로 코드로 확인하면 아래와 같다.*** 155 | 156 | ```python 157 | import sys 158 | print(sys.float_info.epsilon) 159 | ``` 160 | 161 | ```bash 162 | 2.220446049250313e-16 163 | ``` 164 | 165 | Python은 실수 표현에 있어 배정도를 사용하므로, $1.man_{(2)} \times 2^{exp-bias}$ 의 ***가수부 $1.man$에서 $man$에 해당하는 부분에 52비트를 활용한다.*** 166 | 167 | 따라서 위의 엡실론에 대한 출력값은 아래의 과정을 통해서 나온 것이다. 168 | 169 | $$ 170 | 1.0000 \cdots 0000_{(2)}(0: 52개) \times 2^{0} 171 | $$ 172 | 173 | 배정도에서 1.0 다음으로 **표현할 수 있는 수(representable float)** 은 다음과 같다. 174 | 175 | $$ 176 | 1.0000 \cdots 0001_{(2)}(0: 51개, 1: 마지막 비트) \times 2^{0} 177 | $$ 178 | 179 | 두 수의 차이는 다음과 같다. 180 | 181 | $$ 182 | 0.0000 \cdots 0001_{(2)}(0: 51개, 1: 마지막 비트) \times 2^{0} 183 | $$ 184 | 185 | 이 수를 10진수로 바꾸면 엡실론 값이 나온다. 186 | 187 | $$ 188 | 2.20446049250313 \times 10^{-16} 189 | $$ 190 | 191 | ### 5.2 엡실론과 정밀도 192 | 10진수 실수 9.25를 2진수 실수로 바꾸고, 부동소수점으로 표현하면 $1.00101_{(2)} \times 2^{3}$ 이며 ***이 식에서 지수부분인 $2^{3}$ 을 떼내어 엡실론을 곱하면, 이 실수와 다음 표현 가능한 수 사이의 차이를 구할 수 있다.*** 193 | 194 | ```python 195 | import sys 196 | ep = sys.float_info.epsilon 197 | a = 9.25 198 | diff = (2**3) * ep 199 | print(diff) 200 | ``` 201 | 202 | ```bash 203 | 1.7763568394002505e-15 204 | ``` 205 | 206 | ```python 207 | b = a + diff 208 | print(b) 209 | print(a == b) 210 | ``` 211 | 212 | ```bash 213 | 9.250000000000002 214 | False 215 | ``` 216 | 217 | 다음 표현 가능한 수의 차이인 `diff` 보다 더 적은 값을 더하면 이를 표현하지 못함을 알 수 있다. 218 | 219 | ```python 220 | print(a) 221 | half_diff = diff / 2 222 | print(half_diff) 223 | c = a + half_diff 224 | print(a==c) 225 | ``` 226 | 227 | ```bash 228 | 9.25 229 | 8.881784197001252e-16 230 | True 231 | ``` 232 | 233 | ### Application of epsilon 234 | **엡실론(epsilon)** 개념을 이용하면, 아래의 현상을 없앨 수 있는 좋은 comparision function을 만들 수가 있다. 235 | 236 | ```python 237 | a = .1 * 3 238 | b = .3 239 | 240 | print(a == b) 241 | ``` 242 | 243 | ```bash 244 | False 245 | ``` 246 | 247 | 두 수를 비교할 때, 아래와 같은 **상대오차(relative error)** 가 엡실론보다 작으면 서로 같은 수라고 판단하는 function을 만듦으로써 위와 같은 문제를 해결할 수 있다. 248 | 249 | $$ 250 | relative \ error = {\left\vert x - y\right\vert \over \max(\left\vert x \right\vert , \left\vert y \right\vert)} 251 | $$ 252 | 253 | ```python 254 | import sys 255 | def is_equal(a, b): 256 | ep = sys.float_info.epsilon 257 | return abs(a - b) <= max(abs(a), abs(b)) * ep 258 | 259 | a = .1 * 3 260 | b = .3 261 | 262 | print(is_equal(a,b)) 263 | ``` 264 | 265 | ```bash 266 | True 267 | ``` 268 | -------------------------------------------------------------------------------- /Tensorflow/How to implement reusing variables.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to implement reusing variables\n", 8 | "`tf.variable_scope`의 reuse argument를 이용하여, reusing variable (etc. parameter sharing)을 하는 방법을 정리" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "### Setup" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import tensorflow as tf\n", 25 | "from pprint import pprint\n", 26 | "\n", 27 | "sess_config = tf.ConfigProto(gpu_options=tf.GPUOptions(allow_growth=True))" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "### Template 1\n", 35 | "scope를 나누어서 쓸 때, reusing variables를 하는 경우" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "with tf.variable_scope('test', reuse = False):\n", 45 | " w1 = tf.get_variable(name = 'weight_1',\n", 46 | " shape = [2,], dtype = tf.float32,\n", 47 | " initializer = tf.ones_initializer())\n", 48 | " w2 = tf.get_variable(name = 'weight_2', initializer = tf.constant([2.,2.], dtype = tf.float32))\n", 49 | "\n", 50 | "with tf.variable_scope('test', reuse = True):\n", 51 | " w3 = tf.get_variable(name = 'weight_1')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 3, 57 | "metadata": {}, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "w1 : [1. 1.]\n", 64 | "w2 : [2. 2.]\n", 65 | "w3 : [1. 1.]\n" 66 | ] 67 | } 68 | ], 69 | "source": [ 70 | "with tf.Session(config = sess_config) as sess:\n", 71 | " sess.run(tf.global_variables_initializer())\n", 72 | " print('w1 : {}'.format(sess.run(w1)))\n", 73 | " print('w2 : {}'.format(sess.run(w2)))\n", 74 | " print('w3 : {}'.format(sess.run(w3)))" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 4, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stdout", 84 | "output_type": "stream", 85 | "text": [ 86 | "[,\n", 87 | " ]\n" 88 | ] 89 | } 90 | ], 91 | "source": [ 92 | "# weight_1을 reuse 했으므로, variable list에도 두 개가 나타남\n", 93 | "pprint(tf.global_variables())" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "### Template 2\n", 101 | "하나의 scope에서 reusing variables를 하는 경우, `reuse_variables` method를 사용" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 5, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "tf.reset_default_graph()\n", 111 | "\n", 112 | "with tf.variable_scope('test') as scope:\n", 113 | " w1 = tf.get_variable(name = 'weight_1',\n", 114 | " shape = [2,], dtype = tf.float32,\n", 115 | " initializer = tf.ones_initializer())\n", 116 | " w2 = tf.get_variable(name = 'weight_2', initializer = tf.constant([2.,2.], dtype = tf.float32))\n", 117 | " scope.reuse_variables() \n", 118 | " w3 = tf.get_variable(name = 'weight_1')" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 6, 124 | "metadata": {}, 125 | "outputs": [ 126 | { 127 | "name": "stdout", 128 | "output_type": "stream", 129 | "text": [ 130 | "w1 : [1. 1.]\n", 131 | "w2 : [2. 2.]\n", 132 | "w3 : [1. 1.]\n" 133 | ] 134 | } 135 | ], 136 | "source": [ 137 | "with tf.Session(config = sess_config) as sess:\n", 138 | " sess.run(tf.global_variables_initializer())\n", 139 | " print('w1 : {}'.format(sess.run(w1)))\n", 140 | " print('w2 : {}'.format(sess.run(w2)))\n", 141 | " print('w3 : {}'.format(sess.run(w3)))" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 7, 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "[,\n", 154 | " ]\n" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "# weight_1을 reuse 했으므로, variable list에도 두 개가 나타남\n", 160 | "pprint(tf.global_variables())" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "### Template 3\n", 168 | "하나의 scope에서 reusing variables를 하는 경우, reuse argument에 `tf.AUTO_REUSE` 전달\n", 169 | "\n", 170 | "* `tf.AUTO_REUSE`를 활용하면, ***이미 해당하는 variable name이 있는 경우 그 variable의 값을 가져오고, 아닐 경우 새로운 variable을 생성함***\n", 171 | "* ***이 방식으로 활용하는 것을 추천!!***" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 8, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "tf.reset_default_graph()\n", 181 | "\n", 182 | "with tf.variable_scope('test', reuse = tf.AUTO_REUSE):\n", 183 | " w1 = tf.get_variable(name = 'weight_1',\n", 184 | " shape = [2,], dtype = tf.float32,\n", 185 | " initializer = tf.ones_initializer())\n", 186 | " w2 = tf.get_variable(name = 'weight_2', initializer = tf.constant([2.,2.], dtype = tf.float32))\n", 187 | " w3 = tf.get_variable(name = 'weight_1')" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 9, 193 | "metadata": {}, 194 | "outputs": [ 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "w1 : [1. 1.]\n", 200 | "w2 : [2. 2.]\n", 201 | "w3 : [1. 1.]\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "with tf.Session(config = sess_config) as sess:\n", 207 | " sess.run(tf.global_variables_initializer())\n", 208 | " print('w1 : {}'.format(sess.run(w1)))\n", 209 | " print('w2 : {}'.format(sess.run(w2)))\n", 210 | " print('w3 : {}'.format(sess.run(w3)))" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 10, 216 | "metadata": {}, 217 | "outputs": [ 218 | { 219 | "name": "stdout", 220 | "output_type": "stream", 221 | "text": [ 222 | "[,\n", 223 | " ]\n" 224 | ] 225 | } 226 | ], 227 | "source": [ 228 | "# weight_1을 reuse 했으므로, variable list에도 두 개가 나타남\n", 229 | "pprint(tf.global_variables())" 230 | ] 231 | } 232 | ], 233 | "metadata": { 234 | "kernelspec": { 235 | "display_name": "Python 3", 236 | "language": "python", 237 | "name": "python3" 238 | }, 239 | "language_info": { 240 | "codemirror_mode": { 241 | "name": "ipython", 242 | "version": 3 243 | }, 244 | "file_extension": ".py", 245 | "mimetype": "text/x-python", 246 | "name": "python", 247 | "nbconvert_exporter": "python", 248 | "pygments_lexer": "ipython3", 249 | "version": "3.6.5" 250 | } 251 | }, 252 | "nbformat": 4, 253 | "nbformat_minor": 2 254 | } 255 | -------------------------------------------------------------------------------- /Git/Git_basics.md: -------------------------------------------------------------------------------- 1 | # Git basics 2 | [지옥에서 온 Git](https://opentutorials.org/module/2676)의 내용을 command 위주로 정리, 그 외 참고한 Reference는 아래와 같음. 3 | 4 | * Reference 5 | + https://git-scm.com/book/en/v2 6 | + https://www.rlee.ai/apt/git 7 | + https://github.com/Gyubin/TIL/blob/master/ETC/git_gitflow.md 8 | --- 9 | 10 | ### Basic commands 11 | #### Applying version control to project 12 | * `git config` : git의 configuration을 설정, 예를 들면 아래처럼 version control을 하는 주체의 정보를 설정할 수 있다. 13 | 14 | ```bash 15 | # example 16 | git config --global user.name 'aisolab' # 자신의 nickname 17 | git config --global user.email 'bsk0130@gmail.com' # 자신의 email 18 | ``` 19 | 20 | * `git init` : version control을 할 project의 directory로 이동 후 입력 (working directory로 만들기) 21 | + 해당 directory 밑에 **.git** directory가 생성되며, ***이 directory는 version control을 위한 여러 파일을 담고 있음*** 22 | * `git status` : project의 directory의 상태확인 23 | + 어떤 파일을 git이 version control을 하는 지 확인 가능하며, **staging area (ready to commit, index)** 에 어떤 파일이 있는 지 확인 가능 24 | * `git add` : version control을 할 project의 directory 내의 파일들을 추가하여 해당 파일을 git이 tracking, 또는 이미 변경된 파일의 경우 **staging area (ready to commit, index)** 에 넣는 역할을 함 25 | + 이미 작업한 내용들 중에서 선택적으로 commit을 하여, version을 만들 수 있음 26 | * `git commit` : version을 생성하며, commit message (version message)를 기록할 수 있는 editor (eg. vim)을 실행함. commit message를 기록하면 version을 만들어 **repository** 에 넣음 27 | + `git commit -m 'message'` : commit과 동시에 commit message를 작성 28 | + 하나의 작업당 하나의 version을 하나 만드는 것이 rule 29 | 30 | #### Checking the changes in commit history 31 | * `git log` : commit history를 조회, 여러 옵션을 줄 수 있으나 다음의 아래가 유용 32 | + `git log commit_id` : commit_id (commit message에 해당하는 version을 가리키는 id)를 입력하면 해당 version이전부터 해당 version가지의 commit message를 확인할 수 있음 33 | + `git log -p` : 전체 version간의 source 상의 변경점을 확인할 수 있음 34 | + `git log -p -n` : 최근 n개의 version간의 source 상의 변경점을 확인할 수 있음 35 | - Git에서는 **.git/objects** directory에서 object의 형태 (사실상 파일)로 version control을 하는 데에 있어서 필요한 내역을 관리 36 | - object id가 파일 내용을 기반으로 **sha1 hash** 방식으로 암호화되어 생성되고, 이를 이름으로하는 object가 생성됨. `git add`, `git commit` 등의 command가 **.git/objects** directory에 발생시키는 내용은 아래와 같음 37 | - `git add` : **.git/objects** directory에 파일의 내용을 담은 object를 추가함, 이를 **blob** object라고함 38 | - `git commit` : **.git/objects** directory에 **parent** 에 해당하는 version의 object id와 **tree** 에 해당하는 version의 object id를 담은 object를 추가함, 이를 **commit** object라고함 39 | - **.git/objects** directory에 생성되는 object는 모두 **commit** object, **blob** object, **tree** object 중 하나 40 | - **commit** object는 **tree** object의 object id와 **parent** object의 object id를 기록 41 | - **blob** object는 실제 파일의 내용을 기록 42 | - **tree** object는 **blob** object의 object id를 기록 43 | 44 | ```bash 45 | #git log 실행한 결과 (example) 46 | commit 1457925822c829fdd998a17ac3860fbae61f44d6 # commit id (object id) 47 | Author: aisolab # user.name user.email 48 | Date: Thu Jul 12 17:24:38 2018 +0900 49 | 1 # commit message 50 | ``` 51 | 52 | * `git diff` : `git add` 이전의 source상의 변경점을 확인, +++에 해당하는 것이 더 최신의 version 53 | + ***commit을 하기전에 시행한 작업에 대해서 변경점을 확인가능한 용도로도 사용할 수 있음*** 54 | - ***이전 version이 있고, 어떤 작업을 했을 때, `git add` 하기전에 변경점을 확인가능, (working directory의 내용과 staging area의 내용을 비교)*** 55 | + `git diff commit_id1..commit_id2` : commit_id1에 해당하는 version의 source와 commit_id2에 해당하는 version의 source간의 변경점을 확인 56 | - commit_id2에 최신의 version에 해당하는 commit_id를 써야 +++에 최신 version의 source의 변경점이 기록됨 57 | 58 | #### Back to the past 59 | * `git reset` : 특정 과거 시점 이후의 version을 날려서 특정 과거 시점의 version을 최신 version으로 만듦, 아래와 같은 command로 활용 60 | + `git reset commit_id --hard` : 이 때 commit_id에는 과거 특정 시점의 version의 commit_id를 전달 61 | + ***remote repository를 활용하지 않을 때, 즉 local repository를 활용할 때만 사용할 것***, 왜냐하면 이 command는 local repository만 과거시점으로 돌리기 때문 62 | + `git reset` 의 여러가지 option들은 아래의 그림으로 이해!! 63 | 64 | ![Alt text](https://i.imgur.com/aX2spsR.png) 65 | 66 | * `git revert` : 최신 version이 잘 못 되었을 때, 해당 version의 한 시점 과거의 것과 동일한 source인 새로운 version을 만듦, 아래와 같은 command로 활용 67 | + `git revert commmit_id` : commit_id에 가장 최근 commit_id를 넣음, 중간시점의 commit_id를 넣으면 conflict 발생 68 | + ***remote repository를 활용할 때 사용할 것***, remote repository의 시점도 과거로 돌아감 69 | 70 | ### Branching commands 71 | local repository든지 remote repository든지 default branch는 master branch 72 | #### Branching 73 | * `git branch` : branch의 목록을 확인하는 command, option을 주어 여러가지로 방법으로 사용 74 | + `git branch branch_name` : branch_name에 해당하는 branch를 생성 75 | + `git branch -d branch_name` : branch_name에 해당하는 branch를 삭제 76 | + `git branch -D branch_name` : 병합하지않은 branch_name에 해당하는 branch를 강제삭제 77 | * `git checkout` : branch를 전환하는 command, 아래처럼 사용 78 | + `git checkout branch_name` : branch_name에 해당하는 branch로 전환 79 | + ` git checkout -b branch_name` : branch_name에 해당하는 branch를 생성하고, 해당 branch로 전환 80 | 81 | #### Checking the changes between branches 82 | * `git log --branches` : branch들의 commit history를 확인, Head가 가리키는 것이 현재 머물고 있는 branch, option을 주어 여러가지 방법으로 사용 83 | + `git log --branches --graph --decorate` : 각각의 branch들의 commit history에 graph option을 주어 확인하기 편하게 84 | + `git log --branches --graph -p` : 각각의 branch들의 commit history에서 source상의 변경점들을 확인 85 | * `git log branch_name1..branch_name2` : branch 간의 변경점 확인, branch_name2에 기준으로부터 변화한 대상의 branch의 이름을 넣는 것이 유용, option을 주어 여러가지 방법으로 사용 86 | + `git log branch_name1..branch_name2 -p` : branch 간의 source상의 변경점을 확인 87 | * `git diff branch_name1..branch_name2` : branch_name1에 해당하는 branch의 최신 version의 source와 branch_name2에 해당하는 branch의 최신 version간의 source상의 변경점을 확인 88 | 89 | #### Merging 90 | * `git merge` : merge는 source를 합치는 기준이 되는 branch로 checkout한 뒤, 해당 branch에서 아래와 같은 command로 merge 91 | + `git checkout branch_name1` : branche_name1에 해당하는 branch로 checkout 92 | + `git merge branch_name2` : ***(Head가 branch_name1인 상태에서)*** branch_name2의 source 변경점들을 branch_name1로 merge 93 | - template : master가 default branch이므로, Head가 master인 상태에서 `git merge master branch_name` 하면 branch_name에 해당하는 branch의 source 변경점들을 master로 merge 94 | - ***서로 다른 branch에서 같은 파일의 같은 부분을 수정하면? Conflict!*** 95 | - 이 경우 merge시 editor가 나타나게되며 사용자가 알아서 잘 수정! 96 | - merging이 끝나면 `git branch -d branch_name2` 로 branch_name2에 해당하는 branch를 삭제 97 | 98 | ```bash 99 | <<<<<<< HEAD # checkout한 branch (eg. master branch) 100 | hi! master 101 | ======= # branch의 경계, 이 예제에서는 서로 다른 브랜치에서 같은 파일의 같은 부분 (hi! 뒷 부분) 수정해서 충돌 102 | hi! develop 103 | >>>>>>> develop # checkout한 branch로 merge되는 branch (eg. develop branch) 104 | ``` 105 | ```bash 106 | * commit 100c731567079c22620071ed3ece231a0f6a9531 (HEAD -> master) 107 | |\ Merge: 8dcace3 2e33343 108 | | | Author: aisolab 109 | | | Date: Fri Jul 13 16:25:12 2018 +0900 110 | | | 111 | | | Merge branch 'develop' # 위의 code block에서 master 부분을 지우고 git add, git commit 112 | | | 113 | | * commit 2e3334326fc883f8cd3c98b4dfaa8e4d7e180b79 (develop) 114 | | | Author: aisolab 115 | | | Date: Fri Jul 13 16:19:46 2018 +0900 116 | | | 117 | | | develop v2 118 | | | 119 | * | commit 8dcace3636378e29a9d5b01f2fe89cbdec1dbd78 120 | |/ Author: aisolab 121 | | Date: Fri Jul 13 16:20:16 2018 +0900 122 | | 123 | | master v2 124 | | 125 | * commit ccd394d62cb6fcc4a7b2643a2dcbc615d9f85622 126 | Author: aisolab 127 | Date: Fri Jul 13 16:19:05 2018 +0900 128 | 129 | master v1 130 | ``` 131 | 132 | * `git stash` : 다른 브랜치로 checkout을 해야하는데, 아직 현재 브랜치에서 작업이 끝나지 않은 경우 commit을 하기가 애매하므로, ***이 때 작업중이던 파일을 임시로 저장해두고 현재 브랜치의 상태를 마지막 커밋의 상태로 초기화하기위해 사용, 이후 작업중이던 상태로 복구가능, stack을 활용하여 구현*** , `git stash` 는 `git add` 에 의해서 version control 대상으로 추가된 파일에만 적용됨 133 | + `git stash save` : 현재 작업한 상태를 (`git add` 하기 전) 저장해놓고, 이전 commit으로 돌아가기, 작업한 상태를 stack에 저장 134 | + `git stash list` : stack에 저장되어있는 작업상태의 목록을 확인 135 | + `git stash apply` : stack에 저장되어있는 작업상태중 가장 위에 있는 상태를 불러옴 (`git stash list` 목록에 그대로 남아있음) 136 | + `git stash drop` : stack에 저장되어있는 작업상태중 가장 위에 있는 상태를 지움 137 | + `git stash pop` : `git stash apply` 를 하고, `git stash drop` 을 한번에 하는 명령어, stack의 pop이라 생각하면 됨 138 | 139 | -------------------------------------------------------------------------------- /Tensorflow/How to simply use tf.data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to simply use tf.data\n", 8 | "`tf.data` library를 사용하는 방법에 관한 예시, 예시 데이터는 `numpy` library를 이용하여 간단하게 data에 해당하는 `X`, target에 해당하는 `y`를 생성하여 `tf.data` library의 각종 module, function을 이용한다. epoch 마다 validation data에 대해서 validation을 하는 상황을 가정" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "## Template\n", 16 | "for문을 활용, model을 training시 data pipeline으로써 아래의 function과 method를 사용하는 방법에 대한 예시\n", 17 | "- **Dataset class**\n", 18 | " - `tf.data.Dataset.from_tensor_slices`으로 Dataset class의 instance를 생성\n", 19 | " - train data에 대한 Dataset class의 instance, tr_data\n", 20 | " - validation data에 대한 Dataset class의 instance, val_data\n", 21 | " - 아래와 같은 method를 활용하여 training 시 필요한 요소를 지정\n", 22 | " - instance의 `shuffle` method를 활용하여, shuffling\n", 23 | " - instanec의 `batch` method를 활용하여, batch size 지정\n", 24 | " - for문으로 전체 epoch를 control하므로 `repeat` method는 활용하지 않음\n", 25 | "\n", 26 | "\n", 27 | "- **Iterator class**\n", 28 | " - Dataset class의 instance에서 `make_initializable_iterator` method로 Iterator class의 instance를 생성\n", 29 | " - train data에 대한 iterator class의 instance, tr_iterator\n", 30 | " - validation data에 대한 iterator class의 instance, val_iterator\n", 31 | " - 주의사항 : `make_initializable_iterator` method로 Iterator class의 instance를 생성할 경우, random_seed를 고정 X\n", 32 | " - random_seed를 고정할 경우, 서로 다른 epoch의 step 별 mini-batch의 구성이 완전히 똑같아지기 때문\n", 33 | " - Anonymous iterator를 `tf.data.Iterator.from_string_handle`로 생성\n", 34 | " - `string_handle` argument에 `tf.placeholder`를 이용\n", 35 | " - tr_iterator를 활용할 것인지, val_iterator를 활용할 것인지 조절" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "### Setup" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 1, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "1.8.0\n" 55 | ] 56 | } 57 | ], 58 | "source": [ 59 | "import numpy as np\n", 60 | "import tensorflow as tf\n", 61 | "\n", 62 | "print(tf.__version__)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 2, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "(12, 2) (12,)\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "# 전체 데이터의 개수가 12개인 임의의 데이터셋 생성\n", 80 | "X = np.c_[np.arange(12), np.arange(12)]\n", 81 | "y = np.arange(12)\n", 82 | "\n", 83 | "print(X.shape, y.shape)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 3, 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "(8, 2) (8,)\n", 96 | "(4, 2) (4,)\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "# 위의 데이터를 train, validation으로 split\n", 102 | "X_tr = X[:8]\n", 103 | "y_tr = y[:8]\n", 104 | "\n", 105 | "X_val = X[8:]\n", 106 | "y_val = y[8:]\n", 107 | "\n", 108 | "print(X_tr.shape, y_tr.shape)\n", 109 | "print(X_val.shape, y_val.shape)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "### Template" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 4, 122 | "metadata": {}, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "epoch : 3, batch_size : 2, total_steps : 4\n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "n_epoch = 3\n", 134 | "batch_size = 2\n", 135 | "total_steps = int(X_tr.shape[0] / batch_size)\n", 136 | "print('epoch : {}, batch_size : {}, total_steps : {}'.format(n_epoch, batch_size, total_steps))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 5, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "\n", 149 | "\n" 150 | ] 151 | } 152 | ], 153 | "source": [ 154 | "tr_data = tf.data.Dataset.from_tensor_slices((X_tr, y_tr)) # 0th dimension의 size가 같아야\n", 155 | "tr_data = tr_data.shuffle(buffer_size = 30)\n", 156 | "tr_data = tr_data.batch(batch_size = batch_size)\n", 157 | "\n", 158 | "val_data = tf.data.Dataset.from_tensor_slices((X_val, y_val))\n", 159 | "val_data = val_data.shuffle(buffer_size = 30)\n", 160 | "val_data = val_data.batch(batch_size = batch_size)\n", 161 | "\n", 162 | "print(tr_data)\n", 163 | "print(val_data)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 6, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "tr_iterator = tr_data.make_initializable_iterator()\n", 173 | "val_iterator = val_data.make_initializable_iterator()\n", 174 | "\n", 175 | "handle = tf.placeholder(dtype = tf.string)" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 7, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "iterator = tf.data.Iterator.from_string_handle(string_handle = handle,\n", 185 | " output_shapes = tr_iterator.output_shapes,\n", 186 | " output_types = tr_iterator.output_types)\n", 187 | "X_mb, y_mb = iterator.get_next()" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 8, 193 | "metadata": {}, 194 | "outputs": [ 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "epoch : 1 training start\n", 200 | "step : 1\n", 201 | "[[1 1]\n", 202 | " [0 0]] [1 0]\n", 203 | "step : 2\n", 204 | "[[5 5]\n", 205 | " [2 2]] [5 2]\n", 206 | "step : 3\n", 207 | "[[7 7]\n", 208 | " [6 6]] [7 6]\n", 209 | "step : 4\n", 210 | "[[4 4]\n", 211 | " [3 3]] [4 3]\n", 212 | "epoch : 1 training finished\n", 213 | "at epoch : 1, validation start\n", 214 | "step : 1\n", 215 | "[[ 9 9]\n", 216 | " [10 10]] [ 9 10]\n", 217 | "step : 2\n", 218 | "[[11 11]\n", 219 | " [ 8 8]] [11 8]\n", 220 | "validation finished\n", 221 | "epoch : 2 training start\n", 222 | "step : 1\n", 223 | "[[5 5]\n", 224 | " [1 1]] [5 1]\n", 225 | "step : 2\n", 226 | "[[7 7]\n", 227 | " [2 2]] [7 2]\n", 228 | "step : 3\n", 229 | "[[0 0]\n", 230 | " [4 4]] [0 4]\n", 231 | "step : 4\n", 232 | "[[3 3]\n", 233 | " [6 6]] [3 6]\n", 234 | "epoch : 2 training finished\n", 235 | "at epoch : 2, validation start\n", 236 | "step : 1\n", 237 | "[[11 11]\n", 238 | " [ 9 9]] [11 9]\n", 239 | "step : 2\n", 240 | "[[10 10]\n", 241 | " [ 8 8]] [10 8]\n", 242 | "validation finished\n", 243 | "epoch : 3 training start\n", 244 | "step : 1\n", 245 | "[[6 6]\n", 246 | " [2 2]] [6 2]\n", 247 | "step : 2\n", 248 | "[[4 4]\n", 249 | " [5 5]] [4 5]\n", 250 | "step : 3\n", 251 | "[[0 0]\n", 252 | " [7 7]] [0 7]\n", 253 | "step : 4\n", 254 | "[[1 1]\n", 255 | " [3 3]] [1 3]\n", 256 | "epoch : 3 training finished\n", 257 | "at epoch : 3, validation start\n", 258 | "step : 1\n", 259 | "[[ 8 8]\n", 260 | " [11 11]] [ 8 11]\n", 261 | "step : 2\n", 262 | "[[ 9 9]\n", 263 | " [10 10]] [ 9 10]\n", 264 | "validation finished\n" 265 | ] 266 | } 267 | ], 268 | "source": [ 269 | "# n_tr_step, n_val_step 변수와 관련된 코드는 step 수 확인을 위해 넣어놓음\n", 270 | "\n", 271 | "sess = tf.Session()\n", 272 | "tr_handle, val_handle = sess.run([tr_iterator.string_handle(), val_iterator.string_handle()])\n", 273 | "\n", 274 | "for epoch in range(n_epoch):\n", 275 | " \n", 276 | " print('epoch : {} training start'.format(epoch + 1))\n", 277 | " sess.run(tr_iterator.initializer) # make_initail\n", 278 | " n_tr_step = 0\n", 279 | " \n", 280 | " while True:\n", 281 | " try:\n", 282 | " n_tr_step += 1\n", 283 | " X_tmp, y_tmp = sess.run([X_mb, y_mb], feed_dict = {handle : tr_handle})\n", 284 | " print('step : {}'.format(n_tr_step))\n", 285 | " print(X_tmp, y_tmp)\n", 286 | " \n", 287 | " except:\n", 288 | " print('epoch : {} training finished'.format(epoch + 1))\n", 289 | " break\n", 290 | "\n", 291 | " print('at epoch : {}, validation start'.format(epoch + 1)) \n", 292 | " sess.run(val_iterator.initializer)\n", 293 | " n_val_step = 0\n", 294 | " while True:\n", 295 | " try:\n", 296 | " n_val_step += 1\n", 297 | " X_tmp, y_tmp = sess.run([X_mb, y_mb], feed_dict = {handle : val_handle})\n", 298 | " \n", 299 | " print('step : {}'.format(n_val_step))\n", 300 | " print(X_tmp, y_tmp)\n", 301 | " except:\n", 302 | " print('validation finished')\n", 303 | " break" 304 | ] 305 | } 306 | ], 307 | "metadata": { 308 | "kernelspec": { 309 | "display_name": "Python 3", 310 | "language": "python", 311 | "name": "python3" 312 | }, 313 | "language_info": { 314 | "codemirror_mode": { 315 | "name": "ipython", 316 | "version": 3 317 | }, 318 | "file_extension": ".py", 319 | "mimetype": "text/x-python", 320 | "name": "python", 321 | "nbconvert_exporter": "python", 322 | "pygments_lexer": "ipython3", 323 | "version": "3.6.5" 324 | } 325 | }, 326 | "nbformat": 4, 327 | "nbformat_minor": 2 328 | } 329 | -------------------------------------------------------------------------------- /Tensorflow/How to use TFRecord format.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to use TFRecord format\n", 8 | "Tensorflow에서 지원하는 data format인 `TFRecord` format으로 data를 converting하고 이를 `tf.data` 를 사용하여 load하는 방법에 대해서 정리, `TFRecord` format의 특징은 아래와 같음. mnist dataset을 `TFRecord` format으로 converting하고, 이를 `tf.data` 로 읽어들이는 예제\n", 9 | "\n", 10 | "* Property\n", 11 | " + `TFRecord` format의 dataset은 하나의 memory block에 저장되므로, file이 개별로 저장되었을 경우에 비해 io가 빠르다.\n", 12 | " + multi-thread로 활용하기에 더 적합\n", 13 | "* Reference\n", 14 | " + \n", 15 | " + \n", 16 | " + \n", 17 | " + \n", 18 | " + \n", 19 | "\n", 20 | "### Converting dataset to TFRecord format\n", 21 | "dataset을 `TFRecord` format으로 바꾸는 과정에서 필요한 api는 아래와 같다.\n", 22 | "\n", 23 | "* `tf.python_io.TFRecordWriter` class의 instance를 생성 \n", 24 | " + argument\n", 25 | " - path : `TFRecord` format으로 쓸 경로, 확장자는 `.tfrecords`\n", 26 | " - options : `tf.python_io.TFRecordOptions` class의 instance를 전달, 예제 코드에서는\n", 27 | "\n", 28 | " ```python\n", 29 | " options = tf.python_io.TFRecordOptions(compression_type = tf.python_io.TFRecordCompressionType.GZIP)\n", 30 | " ```\n", 31 | "\n", 32 | " + usage\n", 33 | " -  생성된 instance에서 `write` method를 이용하여, `tf.train.Example` 로 생성된 인스턴스를 path에 전달한 경로 parameter에 write한다.\n", 34 | "\n", 35 | "\n", 36 | "* `tf.train.Example` class의 instance를 생성\n", 37 | " + argument\n", 38 | " - Features : `tf.train.Features` class의 instance를 전달\n", 39 | " - `tf.train.Features` 의 argument는 feature로 feature에 `tf.train.Feature` class의 instance가 `dictionary` type으로 모여진 `dictionary` 를 전달하여 instance를 생성한다. 예제 코드에서는 \n", 40 | "\n", 41 | " ```python\n", 42 | " example = tf.train.Example(features = tf.train.Features(feature = {\n", 43 | " 'label' : tf.train.Feature(int64_list = tf.train.Int64List(value = [label])),\n", 44 | " 'image' : tf.train.Feature(bytes_list = tf.train.BytesList(value = [image]))}))\n", 45 | " ```\n", 46 | "\n", 47 | " + usage\n", 48 | " - 생성된 instance에서 `SerializeToString` method를 이용하여 직렬화하고, 이를 `tf.python_io.TFRecordWriter` class에서 생성된 instance의 `write` method로 write한다.\n", 49 | " \n", 50 | "### Loading dataset converted TFRecord with `tf.data`\n", 51 | "`TFRecord` format의 dataset을 `tf.data` 를 이용하여 불러오는 과정에서 필요한 api는 아래와 같다.\n", 52 | "\n", 53 | "* `tf.data.TFRecordDataset` class의 instance를 생성\n", 54 | " + argument\n", 55 | " - filenames : `TFRecord` format의 dataset의 경로\n", 56 | " - compression_type : 불러오는 `TFRecord` format dataset의 compression type을 전달\n", 57 | " + usage\n", 58 | " - 생성된 instance에서 `map` method를 활용, 이 때 `TFRecord` format을 parsing하는 함수를 작성하고 `lambda` 로 선언한 anonymous function을 이용한다. 예제 코드에서는\n", 59 | "\n", 60 | " ```python\n", 61 | " def parse_single_example(record):\n", 62 | " features = {'label' : tf.FixedLenFeature((), tf.int64, 0),\n", 63 | " 'image' : tf.FixedLenFeature((), tf.string, '')}\n", 64 | " parsed_features = tf.parse_single_example(serialized = record, features = features)\n", 65 | " image = tf.decode_raw(parsed_features.get('image'), out_type = tf.float32)\n", 66 | " image = tf.reshape(tensor = image, shape = [28,28,1])\n", 67 | " label = tf.cast(parsed_features.get('label'), dtype = tf.int32)\n", 68 | " return image, label\n", 69 | " ```\n", 70 | "\n", 71 | " ```python\n", 72 | " val = tf.data.TFRecordDataset(filenames = './mnist_validation.tfrecords', compression_type = 'GZIP')\n", 73 | " val = val.map(lambda record : parse_single_example(record))\n", 74 | " ```\n", 75 | " \n", 76 | " + 이후 과정은 `tf.data` 를 일반적으로 사용하는 방식과 같다. 아래의 링크를 참고\n", 77 | " - [How to simply use tf.data](https://github.com/aisolab/TIL/blob/master/Tensorflow/How%20to%20simply%20use%20tf.data.ipynb)" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "### Setup" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 1, 90 | "metadata": { 91 | "scrolled": true 92 | }, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "1.12.0\n" 99 | ] 100 | } 101 | ], 102 | "source": [ 103 | "import os, sys\n", 104 | "import tensorflow as tf\n", 105 | "import numpy as np\n", 106 | "\n", 107 | "print(tf.__version__)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "### Load and Split\n", 115 | "`TFRecord` format으로 변환할 dataset인 mnist dataset을 load" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 2, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "(10000, 28, 28, 1) (10000,) (50000, 28, 28, 1) (10000,)\n" 128 | ] 129 | } 130 | ], 131 | "source": [ 132 | "(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()\n", 133 | "X_train = X_train.astype(dtype = np.float32).reshape(-1,28,28,1)\n", 134 | "y_train = y_train.astype(dtype = np.int32)\n", 135 | "X_test = X_test.astype(dtype = np.float32).reshape(-1,28,28,1)\n", 136 | "y_test = y_test.astype(dtype = np.int32)\n", 137 | "\n", 138 | "# training data에서 10000개의 데이터를 뽑음\n", 139 | "val_indices = np.random.choice(range(X_train.shape[0]), size = 10000, replace = False)\n", 140 | "X_val = X_train[val_indices] \n", 141 | "y_val = y_train[val_indices]\n", 142 | "X_train = np.delete(arr = X_train, obj = val_indices, axis = 0)\n", 143 | "y_train = np.delete(arr = y_train, obj = val_indices, axis = 0)\n", 144 | "print(X_val.shape, y_val.shape, X_train.shape, y_val.shape)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "### Converting dataset to TFRecord format\n", 152 | "mnist example" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 3, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "train = zip(X_train, y_train)\n", 162 | "validation = zip(X_val, y_val)\n", 163 | "test = zip(X_test, y_test)\n", 164 | "\n", 165 | "split = dict(train = train, validation = validation, test = test)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 4, 171 | "metadata": {}, 172 | "outputs": [ 173 | { 174 | "name": "stdout", 175 | "output_type": "stream", 176 | "text": [ 177 | "train was converted to tfrecords\n", 178 | "validation was converted to tfrecords\n", 179 | "test was converted to tfrecords\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "options = tf.python_io.TFRecordOptions(compression_type = tf.python_io.TFRecordCompressionType.GZIP)\n", 185 | "\n", 186 | "for key in split.keys():\n", 187 | " dataset = split.get(key)\n", 188 | " writer = tf.python_io.TFRecordWriter(path = './mnist_{}.tfrecords'.format(key), options = options)\n", 189 | " \n", 190 | " for data, label in dataset:\n", 191 | " image = data.tostring()\n", 192 | " example = tf.train.Example(features = tf.train.Features(feature = {\n", 193 | " 'label' : tf.train.Feature(int64_list = tf.train.Int64List(value = [label])),\n", 194 | " 'image' : tf.train.Feature(bytes_list = tf.train.BytesList(value = [image]))\n", 195 | " }))\n", 196 | " writer.write(example.SerializeToString())\n", 197 | " else:\n", 198 | " writer.close()\n", 199 | " print('{} was converted to tfrecords'.format(key))" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "### Loading dataset converted TFRecord with `tf.data`\n", 207 | "mnist example" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 5, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "def parse_single_example(record):\n", 217 | " features = {'label' : tf.FixedLenFeature((), tf.int64, 0),\n", 218 | " 'image' : tf.FixedLenFeature((), tf.string, '')}\n", 219 | " parsed_features = tf.parse_single_example(serialized = record, features = features)\n", 220 | " image = tf.decode_raw(parsed_features.get('image'), out_type = tf.float32)\n", 221 | " image = tf.reshape(tensor = image, shape = [28,28,1])\n", 222 | " label = tf.cast(parsed_features.get('label'), dtype = tf.int32)\n", 223 | " return image, label" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 6, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "val = tf.data.TFRecordDataset(filenames = './mnist_validation.tfrecords', compression_type = 'GZIP')\n", 233 | "val = val.map(lambda record : parse_single_example(record))\n", 234 | "val = val.batch(batch_size = 2)" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 7, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "val_iterator = val.make_initializable_iterator()\n", 244 | "X, y = val_iterator.get_next()" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 8, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "with tf.Session() as sess:\n", 254 | " sess.run(val_iterator.initializer)\n", 255 | " X_data = sess.run(X)\n", 256 | " y_data = sess.run(y)" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 9, 262 | "metadata": {}, 263 | "outputs": [ 264 | { 265 | "name": "stdout", 266 | "output_type": "stream", 267 | "text": [ 268 | "(2, 28, 28, 1) float32\n", 269 | "(2,) int32\n" 270 | ] 271 | } 272 | ], 273 | "source": [ 274 | "print(X_data.shape, X_data.dtype)\n", 275 | "print(y_data.shape, y_data.dtype)" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "shuffle batch, repeat\n" 285 | ] 286 | } 287 | ], 288 | "metadata": { 289 | "kernelspec": { 290 | "display_name": "Python 3", 291 | "language": "python", 292 | "name": "python3" 293 | }, 294 | "language_info": { 295 | "codemirror_mode": { 296 | "name": "ipython", 297 | "version": 3 298 | }, 299 | "file_extension": ".py", 300 | "mimetype": "text/x-python", 301 | "name": "python", 302 | "nbconvert_exporter": "python", 303 | "pygments_lexer": "ipython3", 304 | "version": "3.6.8" 305 | } 306 | }, 307 | "nbformat": 4, 308 | "nbformat_minor": 2 309 | } 310 | -------------------------------------------------------------------------------- /CS/Class.md: -------------------------------------------------------------------------------- 1 | # Class 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | * Reference 5 | + 6 | 7 | ## 1. 클래스 관계 8 | **클래스(class)** 를 설계하다보면 클래스와 클래스 사이가 어떤 관계를 형성하는 것을 볼 수 있으며, 관계를 나타내는 방법으로 **IS-A** 와 **HAS-A** 있다. 두 가지를 짧게 요약하자면 아래와 같다. 9 | 10 | * **IS-A** 11 | + **Inheritance** 12 | + **자식 클래스(child, derived, sub)** 가 **부모 클래스(super, base, parent)**의 모든 instance variable(instance member), instance method, class variable(class member), class method를 가질 때 13 | + ***위의 모든 variable 또는 method를 덮어쓰거나 유지하면서 다른 것을 추가해야할 때 (Polymorphism)*** 14 | * **HAS-A** 15 | + **Composition** 16 | - 클래스 간 강한 연관관계(strong coupling)를 가지고, 같은 생명 주기(same life cycle)를 가질 때 17 | + **Aggregation** 18 | - 클래스 간 약한 연관관계(weak coupling)를 가지고, 다른 생명 주기(different life cycle)를 가질 때 19 | 20 | ### 1.1 IS-A: 상속 21 | **IS-A** 는 ***"~는 ~의 한 종류다."*** 라고 표현할 수 있으며, **IS-A** 관계를 프로그램에서 표현할 때는 **상속(inheritance)** 을 사용한다. 상속 관계에서 상속을 하는 클래스를 기본(base) 클래스, 부모(parent) 클래스, 슈퍼(super) 클래스라고 하며, 상속을 받는 클래스를 파생(derived) 클래스, 자식(child) 클래스, 서브(sub) 클래스라고 한다. ***상속은 자식 클래스가 부모 클래스의 모든 특성(instance variable, class variable) 또는 기능(instance method, class method) 등을 가지면서 그 외에 다른 특성이나 다른 기능을 가지고 있을 때 활용한다.*** Python으로 부모 클래스인 `Computer` 클래스와 자식 클래스인 `Laptop` 클래스를 구현해보면 아래와 같이 구현할 수 있다. 22 | 23 | ```python 24 | class Computer: 25 | def __init__(self, cpu, ram): 26 | self.cpu = cpu 27 | self.ram = ram 28 | 29 | def browse(self): 30 | print('browse') 31 | 32 | def work(self): 33 | print('work') 34 | 35 | class Laptop(Computer): 36 | def __init__(self, cpu, ram, battery): # method overriding 37 | super().__init__(cpu, ram) 38 | self.battery = battery 39 | 40 | def move(self): 41 | print('move') 42 | ``` 43 | 44 | ```python 45 | com = Computer(cpu = 'intel', ram = '64gb') 46 | lap = Laptop(cpu = 'intel', ram = '16gb', battery = 'lg') 47 | 48 | com.browse() 49 | com.work() 50 | 51 | lap.browse() 52 | lap.work() 53 | lap.move() 54 | ``` 55 | 56 | ```bash 57 | browse 58 | work 59 | browse 60 | work 61 | move 62 | ``` 63 | 64 | ### 1.2 HAS-A: 합성 또는 통합 65 | 프로그램에서 **HAS-A** 관계는 ***~이 ~을 가진다 또는 포함한다.*** **HAS-A** 로 표현되는 두 관계는 각각 **합성(composition)** , **통합(aggregation)** 을 이용하여 표현한다. 66 | 67 | #### 합성(composition) 68 | **합성(composition)** 은 ***각각의 클래스로부터 생성된 인스턴스의 생명주기(life cycle)가 같고 강한 연관관계(strong coupling)을 갖고 있다.*** 예를 들어 컴퓨터와 CPU의 관계라고 볼 수 있으며, Python을 이용하여 아래의 코드처럼 표현할 수 있다. 69 | 70 | ```python 71 | class CPU: 72 | pass 73 | 74 | class RAM: 75 | pass 76 | 77 | class Computer: 78 | def __init__(self): 79 | self.cpu = CPU() 80 | self.ram = RAM() 81 | ``` 82 | 83 | #### 통합(aggregation) 84 | **통합(aggregation)** 은 ***각각의 클래스로부터 생성된 인스턴스의 생명주기(life cycle)이 서로 다르고 약한 연관관계(weak coupling)을 갖고 있다.*** 예를 들어 경찰과 총의 관계라고 볼 수 있으며, Python을 이용하여 아래의 코드처럼 표현할 수 있다. 85 | 86 | ```python 87 | class Gun: 88 | def __init__(self, kind): 89 | self.kind = kind 90 | 91 | def bang(self): 92 | print('bang bang!') 93 | 94 | class Police: 95 | def __init__(self): 96 | self.gun = None 97 | 98 | def acquire_gun(self, gun): 99 | self.gun = gun 100 | 101 | def release_gun(self): 102 | gun = self.gun 103 | self.gun = None 104 | return gun 105 | 106 | def shoot(self): 107 | if self.gun: 108 | self.gun.bang() 109 | else: 110 | print('Unable to shoot') 111 | ``` 112 | 113 | ```python 114 | revolver = Gun('revolver') 115 | new_pol = Police() 116 | new_pol.shoot() 117 | ``` 118 | 119 | ```bash 120 | Unable to shoot 121 | ``` 122 | 123 | ```python 124 | new_pol.acquire_gun(gun = revolver) 125 | revolver = None 126 | new_pol.shoot() 127 | ``` 128 | 129 | ```bash 130 | bang bang! 131 | ``` 132 | 133 | ```python 134 | revolver = new_pol.release_gun() 135 | new_pol.shoot() 136 | ``` 137 | 138 | ```bash 139 | Unable to shoot 140 | ``` 141 | 142 | ## 2. 메소드 오버라이딩과 다형성 143 | **다형성(polymorphism)** 이란 ***"상속 관계에 있는 다양한 클래스의 인스턴스에서 같은 이름의 메소드를 호출할 때, 각 인스턴스가 서로 다르게 구현된 메소드를 호출함으로써 서로 다른 행동(behavior), 기능, 결과를 가져오는 것"*** 을 의미한다. 이를 구현하기위해서는 **파생 클래스(derived class)** 안에서 상속받은 메소드를 다시 구현 하는 것을 **메소드 오버라이딩(method overriding)** 이라고 한다. 144 | 145 | ### 2.1 메소드 오버라이딩 146 | **메소드 오버라이딩(method overriding)** 은 자식클래스가 부모 클래스를 상속할 때, 부모 클래스에 존재하는 class method, instance method 등을 자식클래스에서 다시 구현하는 것을 의미한다. 아래의 예제로 확인할 수 있다. 또한 이를 통해서 같은 이름의 메소드를 호출해도 호출한 인스턴스에 따라 다른결과를 내는 **다형성(polymorphism)** 을 구현한다. 147 | 148 | ```python 149 | class CarOwner: 150 | def __init__(self, name): 151 | self.name = name 152 | 153 | def concentrate(self): 154 | print('{}은 집중하느라 아무것도 못합니다.'.format(self.name)) 155 | 156 | class Car: 157 | def __init__(self, owner_name): 158 | self.car_owner = CarOwner(owner_name) 159 | 160 | def drive(self): 161 | self.car_owner.concentrate() 162 | 163 | class SelfDrivingCar(Car): 164 | # method overriding 165 | # 자식 클래스가 부모 클래스에 이미 있는 메소드를 재정의하는 것! 166 | # 자식 클래스의 메소드의 기능이 달라졌을 때 167 | def drive(self): 168 | print('차가 운전합니다. {}는 지금 놀고있어요.'.format(self.car_owner.name)) 169 | ``` 170 | 171 | ```python 172 | tesla = SelfDrivingCar(owner_name='aisolab') 173 | tesla.drive() 174 | ``` 175 | 176 | ```bash 177 | 차가 운전합니다. aisolab는 지금 놀고있어요. 178 | ``` 179 | 180 | ```python 181 | # 다형성 예시 182 | normal_car = Car('yang') 183 | self_car = SelfDrivingCar('park') 184 | self_car2 = SelfDrivingCar('kim') 185 | 186 | cars = [] 187 | cars.append(normal_car) 188 | cars.append(self_car) 189 | cars.append(self_car2) 190 | 191 | for car in cars: 192 | car.drive() 193 | ``` 194 | 195 | ```bash 196 | yang은 집중하느라 아무것도 못합니다. 197 | 차가 운전합니다. park는 지금 놀고있어요. 198 | 차가 운전합니다. kim는 지금 놀고있어요. 199 | ``` 200 | 201 | ### 2.2 다형성 202 | **다형성(polymorphism)** 을 구현하는 데, 가장 큰 개념이 되는 부모 클래스로는 인스턴스를 생성하지 못하게 **추상 클래스(abstract class)** 기법을 이용할 수 있다. 추상 클래스 기법은 추상 클래스로 선언한 부모 클래스에서 정의된 instance method, class method 들 중, **추상 메소드(abstract method)** , **추상 클래스 메소드(abstract class method)** 로 정의된 메소드의 경우 상속받는 자식 클래스에서 반드시 메소드 오버라이딩을 해야한다. 그렇지않으면 자식 클래스도 추상 클래스가 되어 인스턴스를 만들 수가 없다. Python으로 작성한 예제를 통해서 확인해 볼 수 있다. 203 | 204 | ```python 205 | # 아래의 방법으로 추상클래스를 만들자 206 | from abc import ABCMeta, abstractmethod, abstractclassmethod 207 | 208 | class Animal(metaclass = ABCMeta): 209 | 210 | def die(self): 211 | print('time to die') 212 | 213 | @abstractmethod # 자식클래스에서 반드시 재정의해야한다. 214 | def eat(self): # abstract instance method 215 | pass 216 | ``` 217 | 218 | ```python 219 | animals = [] 220 | lion = Lion() 221 | deer = Deer() 222 | human = Human() 223 | 224 | animals.append(lion) 225 | animals.append(deer) 226 | animals.append(human) 227 | 228 | for animal in animals: 229 | animal.eat() 230 | animal.die() 231 | ``` 232 | 233 | ```bash 234 | eat meat 235 | time to die 236 | eat grass 237 | time to die 238 | eat meat and grass 239 | time to die 240 | ``` 241 | 242 | ## 3. 클래스 설계 예제 243 | 클래스를 계층적 구조로 설계할 때는 아래의 두 가지를 고려해야한다. 244 | 245 | * 코드 재사용을 위해 공통 부분을 부모 클래스로 묶는다. 246 | * 부모 클래스가 추상 클래스인 경우를 제외하고, 자식 클래스에서 부모 클래스의 여러 메소드를 메소드 오버라이딩한다면 자식 클래스를 만들지 않는 것이 좋다. 247 | 248 | 예를 들어, 게임 세계의 Character와 Monster들을 클래스 계층적 구조로 아래와 섹션에서 소개하는 것들과 같이 구현할 수 있다. 249 | 250 | ### 3.1 Character 클래스 만들기 251 | **추상 클래스(abstract class)** 로 `Character` 클래스를 구현하고, 해당 클래스를 이후 섹션에서 구현할 `Player` 클래스와 `Monster` 클래스가 상속 252 | 253 | ```python 254 | from abc import ABCMeta, abstractmethod 255 | 256 | # 자식 클래스가 상속 받을 추상 클래스 257 | class Character(metaclass = ABCMeta): 258 | def __init__(self, name, hp, power): 259 | self.name = name 260 | self.hp = hp 261 | self.power = power 262 | 263 | # abstact method에서 거의 함수 signature를 정의함 264 | # abstract instance method를 상속받는 자식클래스는 무조건 method overriding으로 재정의해야함 265 | @abstractmethod 266 | def attack(self, other, attackkind): 267 | pass 268 | 269 | @abstractmethod 270 | def get_damage(self, power, attackkind): 271 | pass 272 | 273 | def __str__(self): 274 | return '{} : {}'.format(self.name, self.hp) 275 | ``` 276 | 277 | ### 3.2 Player 클래스 만들기 278 | `Player` 클래스는 추상 클래스인 `Character` 클래스를 상속받아서 아래와 같이 구현 279 | 280 | ```python 281 | class Player(Character): 282 | def __init__(self,name = 'player', hp = 100, power = 50, *skills): 283 | super().__init__(name, hp, power) 284 | self.skills = list(skills) 285 | 286 | def attack(self, monster, attackkind): # 287 | ''' 288 | 만약 attackkind가 skills에 있다면 monster 공격 289 | message passing으로 구현 290 | ''' 291 | if attackkind in self.skills: 292 | monster.get_damage(power = self.power, attackkind = attackkind) 293 | else: 294 | return 295 | 296 | def get_damage(self, power, attackkind): 297 | ''' 298 | monster의 attackkind가 플레이어의 skills 목록에 있으면 power // 2 만큼 데미지를 입는다. 299 | 아니면 그대로 hp -= power 300 | ''' 301 | if attackkind in self.skills: 302 | self.hp -= power // 2 303 | else: 304 | self.hp -= power 305 | ``` 306 | 307 | ### 3.3 Monster, IceMonster, FireMonster 클래스 만들기 308 | `Monster` 클래스는 추상 클래스인 `Character` 클래스를 상속하고, `IceMonster` , `FireMonster` 클래스는 `Monster` 클래스를 상속 309 | 310 | ```python 311 | ''' 312 | 몬스터 313 | attack -> 몬스터의 attackkind가 player의 attackkind와 같으면 공격 아니면 공격안함 314 | get_damage -> 만약 'ICE' 공격을 받았는데 나의 self.attackkind = 'ICE' -> skdml self.hp += power 315 | -> 그게 아니면 나의 self.hp -= power 316 | ''' 317 | 318 | class Monster(Character): 319 | def __init__(self, name, hp, power): 320 | super().__init__(name, hp, power) 321 | self.attackkind = self.__class__.__name__.replace('Monster', '') 322 | 323 | 324 | def attack(self, player, attackkind): 325 | if self.attackkind == attackkind: 326 | player.get_damage(power = self.power, attackkind = self.attackkind) 327 | else: 328 | return 329 | 330 | def get_damage(self, power, attackkind): 331 | if self.attackkind == attackkind: 332 | self.hp += power 333 | else: 334 | self.hp -= power 335 | 336 | class IceMonster(Monster): 337 | pass 338 | 339 | class FireMonster(Monster): 340 | pass 341 | ``` 342 | 343 | 위의 각각의 클래스에 대한 구현들이 잘 구현되었는 지 아래의 코드를 통해서 확인할 수 있다. 344 | 345 | ```python 346 | player = Player('aisolab', 100, 50, 'Fire','Ice') 347 | monsters = [IceMonster(name = 'IceMonster', hp = 100, power = 30), 348 | FireMonster(name = 'FireMonster', hp = 150, power = 50)] 349 | 350 | for monster in monsters: 351 | print(monster) 352 | ``` 353 | 354 | ```bash 355 | IceMonster : 100 356 | FireMonster : 150 357 | ``` 358 | 359 | ```python 360 | for monster in monsters: 361 | player.attack(monster, 'Fire') 362 | for monster in monsters: 363 | print(monster) 364 | ``` 365 | 366 | ```bash 367 | IceMonster : 50 368 | FireMonster : 200 369 | ``` 370 | 371 | ## 4. 연산자 오버로딩 372 | **연산자 오버로딩(operator overloading)** 은 클래스안에서 메소드로 **연산자(operator)** 를 새롭게 구현하는 것으로 다형성의 특별한 형태이다. Python에서는 클래스를 정의할 때, ***산술 연산자와 논리 연산자 등 다양한 연산자들을 메소드 오버라이딩하여 재정의하면 된다.*** 예를 들어 Python에서 `__add__` 메소드에 대해서 메소드오버라이딩을 하면 `+` operator로 인스턴스간의 연산을 할 수 있다. 373 | 374 | ```python 375 | class Point: 376 | def __init__(self, x, y): 377 | self._x = x 378 | self._y = y 379 | 380 | def __add__(self, other): 381 | return Point(self._x + other._x, self._y + other._y) 382 | 383 | def __str__(self): 384 | return '({}, {})'.format(self._x, self._y) 385 | ``` 386 | 387 | ```python 388 | p1 = Point(1,2) 389 | p2 = Point(3,4) 390 | print(p1, p2) 391 | ``` 392 | 393 | ```bash 394 | (1, 2) (3, 4) 395 | ``` 396 | 397 | ```python 398 | p3 = p1 + p2 399 | print(p3) 400 | ``` 401 | 402 | ```bash 403 | (4, 6) 404 | ``` -------------------------------------------------------------------------------- /CS/Function.md: -------------------------------------------------------------------------------- 1 | # Function 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | * Reference 5 | + 6 | + 7 | + 8 | + 9 | 10 | ## 1. 함수를 시작하기 전에 11 | ### 1.1 자료 구조 미리 엿보기 12 | **함수(function)** 정의된 이후 **호출(call)** 될 때, **스택 프레임(stack frame)** 이라는 함수 내부에서 생성되는 **지역변수(local variable)** 가 저장되는 메모리 공간이 생기는데, 이름에서 보듯이 자료구조의 **스택(stack)** 과 매우 유사하게 작동한다. 13 | 주의 해야할 점은 위의 이야기는 Python에서는 거의 유사하나 조금 다르게 동작한다. 그 이유는 Python은 **변수(variable)** 는 그저 이름일 뿐이고, 실제로는 다른 메모리 공간에 저장된 **값 객체(value object)** 를 가리킬 뿐이기 때문이다. 일반적으로 다른 언어에서는 변수 자체가 메모리 공간이다. 14 | 15 | ### 1.2 전역변수와 지역변수 16 | * **전역변수(global variable)** : ***전체 영역에서 접근할 수 있는 변수*** 로 변수에 값을 할당함과 동시에 (Python에서는 변수가 값 객체를 가리킴과 동시에) **글로벌 프레임(global frame)** 에 할당된다. 17 | * **지역변수(local variable)** : ***함수 내부에서 접근할 수 있는 변수*** 로 함수가 호출됨과 동시에 함수의 이름을 **namespace** 으로하는 스택 프레임에 할당된다. ***지역 변수는 함수 외부에서는 접근할 수 없고 함수가 호출될 때 생성되었다가 호출이 끝나면 사라진다.*** 18 | 19 | Python에서는 아래의 코드로 확인이 가능하다. 아래의 코드에 대한 글로벌 프레임과 스택 프레임의 모습을 아래를 클릭하여 확인할 수 있다. 물론 Python에서의 동작이라는 점을 감안한다. 20 | 21 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=g_var%20%3D%2010%0Adef%20func%28%29%3A%0A%20%20%20%20g_var%20%3D%2020%0A%20%20%20%20print%28'g_var%20%3D%20%7B%7D%20in%20function'.format%28g_var%29%29%0A%20%20%20%20%0Afunc%28%29%0Aprint%28'g_var%20%3D%20%7B%7D%20in%20main'.format%28g_var%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 22 | 23 | ```python 24 | g_var = 10 25 | def func(): 26 | g_var = 20 27 | print('g_var = {} in function'.format(g_var)) 28 | 29 | func() 30 | print('g_var = {} in main'.format(g_var)) 31 | ``` 32 | 33 | ```bash 34 | g_var = 20 in function 35 | g_var = 10 in main 36 | ``` 37 | 38 | 함수 내부에서 전역변수에 접근하는 것은 가능하나 수정하는 것은 불가능하다. 만약 함수 내부에서 전역변수를 수정하고 싶다면, Python에서는 `global` 키워드를 활용한다. 아래의 코드로 확인가능하다. 39 | 40 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=g_var%20%3D%2010%0Adef%20func%28%29%3A%0A%20%20%20%20%23%20global%20variable%20g_var%EC%9D%84%20%ED%95%A8%EC%9C%BC%EB%A1%9C%EC%8D%A8%20%ED%95%A8%EC%88%98%20%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C%20%0A%20%20%20%20%23%20global%20variable%20g_var%EC%9D%98%20%EC%9D%B4%EB%A6%84%EC%9D%84%20%EC%82%AC%EC%9A%A9%ED%95%A0%20%EC%88%98%20%EC%9E%88%EA%B3%A0%0A%20%20%20%20%23%20g_var%EB%A1%9C%20%EC%83%88%EB%A1%AD%EA%B2%8C%20%EC%83%9D%EC%84%B1%EB%90%9C%20%EA%B0%92%20%EA%B0%9D%EC%B2%B4%2020%EC%9D%84%20%EA%B0%80%EB%A6%AC%ED%82%B4%0A%20%20%20%20global%20g_var%0A%20%20%20%20g_var%20%3D%2020%0A%20%20%20%20print%28'g_var%20%3D%20%7B%7D%20in%20function'.format%28g_var%29%29%0A%20%20%20%20%0Afunc%28%29%0Aprint%28'g_var%20%3D%20%7B%7D%20in%20main'.format%28g_var%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 41 | 42 | ```python 43 | g_var = 10 44 | def func(): 45 | # global variable g_var을 함으로써 함수 내부에서 46 | # global variable g_var의 이름을 사용할 수 있고 47 | # g_var로 새롭게 생성된 값 객체 20을 가리킴 48 | global g_var 49 | g_var = 20 50 | print('g_var = {} in function'.format(g_var)) 51 | 52 | func() 53 | print('g_var = {} in main'.format(g_var)) 54 | ``` 55 | 56 | ```bash 57 | g_var = 20 in function 58 | g_var = 20 in main 59 | ``` 60 | 61 | 또 살펴 볼 것은 ***함수(외부 함수) 내부에 함수(내부 함수)가 정의된 경우, 내부 함수가 아닌 외부 함수에 정의된 변수는 전역변수도 아니고, 내부 함수의 지역변수도 아니다.*** 이러한 경우를 통칭하여 **non-local variable** 이라고 칭한다. 이는 Python에서의 네이밍이며 다른 언어에서는 다른 이름일 수 있다. 아래의 코드로 확인가능하다. 62 | 63 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=a%20%3D%2010%20%23%20global%0Adef%20outer%28%29%3A%0A%20%20%20%20b%20%3D%2020%20%23%20non-local%0A%20%20%20%20def%20inner%28%29%3A%0A%20%20%20%20%20%20%20%20c%20%3D%2030%20%23%20non-local%0A%20%20%20%20%20%20%20%20def%20deeper%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20d%20%3D%2040%20%23%20local%0A%20%20%20%20%20%20%20%20%20%20%20%20print%28a,%20b,%20c,%20d%29%0A%20%20%20%20%20%20%20%20deeper%28%29%0A%20%20%20%20inner%28%29%0A%20%20%20%20%0Aouter%28%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 64 | 65 | ```python 66 | a = 10 # global 67 | def outer(): 68 | b = 20 # non-local 69 | def inner(): 70 | c = 30 # non-local 71 | def deeper(): 72 | d = 40 # local 73 | print(a, b, c, d) 74 | deeper() 75 | inner() 76 | 77 | outer() 78 | ``` 79 | 80 | ```bash 81 | 10 20 30 40 82 | ``` 83 | 84 | 위의 코드에서 보면 `outer` 함수 내부에 정의된 `inner` 함수, `inner` 함수 내부에 정의된 `deeper` 함수의 입장에서 변수 `b`, 변수 `c` 전역변수가 아니고 non-local variable이며, `deeper` 함수 내부에서 변수 `b`와 변수 `c`를 수정하려면 `nonlocal` 키워드를 활용한다. 아래의 코드로 확인가능하다. 85 | 86 | [아래의 예시에 대한 동작 확인](http://pythontutor.com/visualize.html#code=a%20%3D%2010%20%23%20global%0Adef%20outer%28%29%3A%0A%20%20%20%20b%20%3D%2020%20%23%20non-local%0A%20%20%20%20def%20inner%28%29%3A%0A%20%20%20%20%20%20%20%20c%20%3D%2030%20%23%20non-local%0A%20%20%20%20%20%20%20%20def%20deeper%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nonlocal%20b,%20c%0A%20%20%20%20%20%20%20%20%20%20%20%20b%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20c%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20d%20%3D%2040%20%23%20local%0A%20%20%20%20%20%20%20%20%20%20%20%20print%28a,%20b,%20c,%20d%29%0A%20%20%20%20%20%20%20%20deeper%28%29%0A%20%20%20%20inner%28%29%0A%20%20%20%20%0Aouter%28%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) 87 | 88 | ```python 89 | a = 10 # global 90 | def outer(): 91 | b = 20 # non-local 92 | def inner(): 93 | c = 30 # non-local 94 | def deeper(): 95 | nonlocal b, c 96 | b += 1 97 | c += 1 98 | d = 40 # local 99 | print(a, b, c, d) 100 | deeper() 101 | inner() 102 | 103 | outer() 104 | ``` 105 | 106 | ```bash 107 | 10 21 31 40 108 | ``` 109 | 110 | ## 2. 인자 전달 방식에 따른 분류 111 | 함수는 **인자(argument)** 에 **매개변수(parameter)** 를 전달하는 방식에 크게 **값에 의한 전달(call by value)** , **참조에 의한 전달(call by reference)** 등으로 나뉜다. Python에서는 변수는 이름일 뿐이고 그 저 **값 객체(value object)** 를 가리킨다는 특징이 있어서, 함수의 인자에 매개변수를 전달하는 방식이 위의 두 가지가 아닌 **객체 참조에 의한 전달(call by object reference)** 를 따른다. 이는 **call by assignment** 라고도 불린다. 112 | 113 | ### 2.1 값에 의한 전달 114 | 아래의 C++ 코드의 실행결과를 보면, **값에 의한 전달(call by value)** 가 이루어지고 있음을 알 수 있다. `main` 함수가 호출되어 스택 프레임이 쌓이는 과정을 기술해보면, `main` 함수의 스택 프레임의 변수 x에 10이 할당되고, 이후 `change_value` 함수가 호출되면서 `change_value` 함수의 스택 프레임의 변수 val에는 20이 할당된다. 그 다음 `change_value` 함수의 스택 프레임의 변수 x에 10이 할당된다. ***그 이후 `change_value` 함수 내부에 `x = val;` 코드가 실행되면서 `change_value` 함수의 스택 프레임의 변수 val에 담긴 값만 복사하여 `change_value` 함수의 스택 프레임의 변수 x에 할당한다.*** `change_value` 함수가 실행을 마치면 `change_value` 함수의 스택프레임을 반환하기 때문에, 아래와 같은 출력결과가 나온다. 115 | 116 | [아래의 코드에 대한 동작 확인](http://pythontutor.com/visualize.html#code=%23include%20%3Cstdio.h%3E%0A%0Avoid%20change_value%28int%20x,%20int%20val%29%20%7B%0A%20%20%20%20x%20%3D%20val%3B%0A%20%20%20%20printf%28%22x%20%3A%20%25d%20in%20change_value%20%5Cn%22,%20x%29%3B%0A%7D%0A%0Aint%20main%28void%29%20%7B%0A%20%20%20%20int%20x%20%3D%2010%3B%0A%20%20%20%20change_value%28x,%2020%29%3B%0A%20%20%20%20printf%28%22x%20%3A%20%25d%20in%20main%20%5Cn%22,%20x%29%3B%0A%7D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp&rawInputLstJSON=%5B%5D&textReferences=false) 117 | 118 | ```cpp 119 | #include 120 | 121 | void change_value(int x, int val) { 122 | x = val; 123 | printf("x : %d in change_value \n", x); 124 | } 125 | 126 | int main(void) { 127 | int x = 10; 128 | change_value(x, 20); 129 | printf("x : %d in main \n", x); 130 | } 131 | ``` 132 | 133 | ```bash 134 | x : 20 in change_value 135 | x : 10 in main 136 | ``` 137 | 138 | ### 2.2 참조에 의한 전달 139 | 아래의 C++ 코드의 실행결과를 보면, **참조에 의한 전달(call by reference)** 가 이루어지고 있음을 알 수 있다. `main` 함수가 호출되어 스택 프레임이 쌓이는 과정을 기술해보면, `main` 함수의 스택 프레임의 변수 x에 10이 할당되고, `change_value` 함수가 호출되면서 `change_value` 함수의 스택 프레임의 변수 val에는 20이 할당된다. 그 다음 인자에 매개변수로 `&x` 를 전달하는데 이는 `main` 함수의 스택 프레임의 변수 x의 첫 번째 바이트 주소값을 전달한다는 의미이며, 고로 `change_value` 함수의 스택 프레임의 변수 x에 이 주소값이 할당된다. (`change_value` 함수의 스택 프레임의 변수 x가 `main` 함수의 스택 프레임의 변수 x를 가리키는 상태 ) 다음 `change_value` 함수의 내부에서 `*x = val` 실행되므로, `main` 함수의 스택 프레임의 변수 x에 `change_value` 함수의 스택 프레임의 변수 val에 담긴 값 20을 복사하여 할당한다. `change_value` 함수가 실행을 마치면 `change_value` 함수의 스택프레임을 반환하기 때문에, 아래와 같은 출력결과가 나온다. 140 | 141 | [아래의 코드에 대한 동작 확인](http://pythontutor.com/visualize.html#code=%23include%20%3Cstdio.h%3E%0A%0Avoid%20change_value%28int%20*%20x,%20int%20val%29%20%7B%0A%20%20%20%20*x%20%3D%20val%3B%0A%20%20%20%20printf%28%22x%20%3A%20%25d%20in%20change_value%20%5Cn%22,%20*x%29%3B%0A%7D%0A%0Aint%20main%28void%29%20%7B%0A%20%20%20%20int%20x%20%3D%2010%3B%0A%20%20%20%20change_value%28%26x,%2020%29%3B%0A%20%20%20%20printf%28%22x%20%3A%20%25d%20in%20main%20%5Cn%22,%20x%29%3B%0A%7D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp&rawInputLstJSON=%5B%5D&textReferences=false) 142 | 143 | ```cpp 144 | #include 145 | 146 | void change_value(int * x, int val) { 147 | *x = val; 148 | printf("x : %d in change_value \n", *x); 149 | } 150 | 151 | int main(void) { 152 | int x = 10; 153 | change_value(&x, 20); 154 | printf("x : %d in main \n", x); 155 | } 156 | ``` 157 | 158 | ```bash 159 | x : 20 in change_value 160 | x : 20 in main 161 | ``` 162 | 163 | ### 2.3 객체 참조에 의한 전달 164 | **객체 참조에 의한 전달(call by object reference)** 는 Python에서 함수의 인자에 매개변수를 전달하는 방식으로 사실은 Python의 변수는 그저 이름을 뿐이며, 변수는 **값 객체(value object)** 를 가리킨다는 사실만 상기하고 있으면, 매우 이해하기 쉽다. 그렇기 때문에 값 객체의 특성을 이해하는 것이 중요하며, Python에서 값 객체는 **immutable**과 **mutable** 의 두 종류가 있으며 아래와 같다. 165 | 166 | * **immutable** 167 | + number(int, float), str, tuple 168 | * **mutable** 169 | + list, dictionary, set 170 | 171 | **immutable** 한 값 객체는 ***변경 또는 수정할 수 없으며, 변경 또는 수정하는 것처럼 보이는 경우는 항상 새로운 값 객체를 생성하여 가리키는 것일 뿐이다.*** 아래의 코드로 확인할 수 있다. 172 | 173 | * number(int, float) 174 | 175 | ```python 176 | a = 1 177 | b = 1 178 | print('a : {}, b : {}'.format(hex(id(a)), hex(id(b)))) 179 | 180 | a +=1 181 | print('a : {}'.format(hex(id(a)))) 182 | ``` 183 | 184 | ```bash 185 | a : 0x10e29b210, b : 0x10e29b210 186 | a : 0x10e29b230 187 | ``` 188 | 189 | * str 190 | 191 | ```python 192 | a = 's' 193 | b = 's' 194 | print('a : {}, b : {}'.format(hex(id(a)), hex(id(b)))) 195 | 196 | a += 'f' 197 | print('a : {}'.format(hex(id(a)))) 198 | ``` 199 | 200 | ```bash 201 | a : 0x10cde1ca8, b : 0x10cde1ca8 202 | a : 0x10ef446c0 203 | ``` 204 | 205 | * tuple 206 | sequence type인 튜플은 각각 element가 가리키는 값 객체가 같아도 하나로 묶어서 튜플을 생성할 때 서로 다른 값 객체로 생성된다. 207 | 208 | ```python 209 | a = 1,2 210 | b = 1,2 211 | print('b[0] : {}, b[1] : {}'.format(hex(id(b[0])), hex(id(b[1])))) 212 | print('a[0] : {}, a[1] : {}'.format(hex(id(a[0])), hex(id(a[1])))) 213 | print('a : {}, b : {}'.format(hex(id(a)), hex(id(b)))) 214 | ``` 215 | 216 | ```bash 217 | b[0] : 0x10e2b2210, b[1] : 0x10e2b2230 218 | a[0] : 0x10e2b2210, a[1] : 0x10e2b2230 219 | a : 0x11045c788, b : 0x11045fc48 220 | ``` 221 | 222 | **mutable** 한 값 객체는 ***변경 또는 수정이 가능하므로, 이미 가리키고 있는 값 객체가 변한다.*** 아래의 코드로 확인할 수 있다. 223 | 224 | * list 225 | 226 | ```python 227 | a = [1,2] 228 | b = [1,2] 229 | print('a[0] : {}, a[1] : {}'.format(hex(id(a[0])), hex(id(a[1])))) 230 | print('b[0] : {}, b[1] : {}'.format(hex(id(b[0])), hex(id(b[1])))) 231 | print('a : {}, b : {}'.format(hex(id(a)), hex(id(b)))) 232 | 233 | a[0] += 2 234 | print('a[0]: {}'.format(hex(id(a[0])))) 235 | print('a : {}'.format(hex(id(a)))) 236 | ``` 237 | 238 | ```bash 239 | a[0] : 0x1034dc210, a[1] : 0x1034dc230 240 | b[0] : 0x1034dc210, b[1] : 0x1034dc230 241 | a : 0x105841508, b : 0x1058414c8 242 | a[0]: 0x1034dc250 243 | a : 0x105841508 244 | ``` 245 | 246 | * dictionary 247 | 248 | ```python 249 | a = dict(a = 1, b = 2) 250 | b = dict(a = 1, b = 2) 251 | 252 | print('a["a"] : {}, a["b"] : {}'.format(hex(id(a.get('a'))),hex(id(a.get('b'))))) 253 | print('b["a"] : {}, b["b"] : {}'.format(hex(id(b.get('a'))),hex(id(b.get('b'))))) 254 | print('a : {}, b : {}'.format(hex(id(a)), hex(id(b)))) 255 | 256 | a.update({'c' : 3}) 257 | print('a : {}'.format(hex(id(a)))) 258 | ``` 259 | 260 | ```bash 261 | a["a"] : 0x101236210, a["b"] : 0x101236230 262 | b["a"] : 0x101236210, b["b"] : 0x101236230 263 | a : 0x10357b5a0, b : 0x10357b3f0 264 | a : 0x10357b5a0 265 | ``` 266 | 267 | * set 268 | 269 | ```python 270 | a = set([1,2,3]) 271 | print(a) 272 | print('a : {}'.format(hex(id(a)))) 273 | 274 | a.update([3,4]) 275 | print(a) 276 | print('a : {}'.format(hex(id(a)))) 277 | ``` 278 | 279 | ```bash 280 | {1, 2, 3} 281 | a : 0x1035814a8 282 | {1, 2, 3, 4} 283 | a : 0x1035814a8 284 | ``` 285 | 286 | Python에서 함수를 호출할 때, **객체참조에 의한 전달**로 함수의 인자에 매개변수를 전달하는 방식은 ***결국 인자에 어떤 값 객체를 전달하느냐에 따라 동작방식이 다르며*** , 코드를 통해서 확인하면 아래와 같다. 287 | 288 | * **immutable** 한 값 객체를 함수의 인자에 전달하는 경우 289 | 아래의 예제를 분석해보면, `global frame` 에 `global variable x` 가 생기고, 이 `global variable x` 가 메모리공간에 저장된 **immutable** 한 값 객체 `10` 을 가리킨다. `change_value` 함수가 호출되면 `change_value` 함수명을 이름으로하는 `stack frame` 에 `local variable value` 가 형성되고, 메모리공간에 저장된 **immutable** 한 값 객체 `20` 을 가리킨다. 바로 이어서 `local variable x` 가 형성되고, 메모리공간에 저장된 **immutable** 값 객체 `10` 을 가리킨다. `change_value` 함수 내부의 코드 `x = value` 가 실행되면서 `local variable value` 가 가리키고 있는 **immutable** 한 값 객체 `20` 을 `local variable x` 도 같이 가리킨다. 그 후 함수가 종료되면 `stack frame` 을 반환한다. `global variable x` 는 여전히 **immutable** 한 값 객체 `10` 을 가리키고 있기 때문에, 출력 결과는 아래와 같다. 290 | 291 | ```python 292 | def change_value(x, value): 293 | x = value 294 | print('x : {} in function'.format(x)) 295 | 296 | if __name__ == '__main__': 297 | x = 10 298 | change_value(x, 20) 299 | print('x : {} in main'.format(x)) 300 | ``` 301 | 302 | ```bash 303 | x : 20 in function 304 | x : 10 in main 305 | ``` 306 | 307 | * **mutable** 한 값 객체를 함수의 인자에 전달할 경우 308 | 아래의 예제를 분석해보면, `global frame` 에 할당된 `global variable li` 는 메모리공간에 저장된 **mutable** 한 값 객체 `[0,1,2,3]` 을 가리킨다. `func` 함수가 호출되면 `func` 함수명을 이름으로하는 `stack frame` 에 `local variable li` 가 형성되고, 이는 `global variable li` 가 가리키는 **mutable** 한 값 객체 `[0,1,2,3]` 을 가리킨다. 동일한 값 객체를 `local variable li` , `global variablie li` 가 가리키고 있지만 **mutable** 한 값 객체이기 때문에, **global** 키워드가 없어도 `func` 함수 내부에서 수정이 가능하다. 함수 실행이 완료되면 `stack frame` 을 반환한다. `global variable li` 가 `func` 함수 내부에서 `li[0] = Talk is cheap. Show me the code.` 이 실행되어 수정된 값 객체 `li = ['Talk is cheap. Show me the code,1,2,3]` 을 가리키게 되기 때문에, 아래와 같은 출력이 나온다. 309 | 310 | 311 | ```python 312 | def func(li): 313 | li[0] = 'Talk is cheap. Show me the code.' 314 | print('{} in function'.format(li)) 315 | 316 | if __name__ == '__main__': 317 | li = [0,1,2,3] 318 | func(li) 319 | print('{} in main'.format(li)) 320 | ``` 321 | 322 | ```bash 323 | ['Talk is cheap. Show me the code.', 1, 2, 3] in function 324 | ['Talk is cheap. Show me the code.', 1, 2, 3] in main 325 | ``` 326 | -------------------------------------------------------------------------------- /CS/Object-oriented programming.md: -------------------------------------------------------------------------------- 1 | # Object-oriented programming 2 | 본 내용은 [양태환](https://github.com/ythwork)님의 저서 **컴퓨터 사이언스 부트캠프 with 파이썬** 을 읽고 요약정리한 내용 3 | 4 | * Reference 5 | + 6 | + 7 | + 8 | 9 | ## 1. 프로그래밍 패러다임 10 | 다양한 프로그래밍 패러다임이 있으나 현재 대표적인 프로그래밍 패러다임으로는 아래의 세 가지가 있다. 11 | 12 | * **프로그래밍 패러다임** 13 | + 절차지향 프로그래밍(procedural programming) 14 | + 객체지향 프로그래밍(object-oriented programming) 15 | + 함수형 프로그래밍(functional programming) 16 | 17 | ## 2. 절차지향 프로그래밍 18 | 절차를 의미하는 **프로시져(procedure)** 는 서브 루틴(subroutine), 메소드(method), 함수(function) 등으로 불리며 ***한번 정의해두면 어디서든 다시 호출해 사용할 수 있다.*** 함수를 작성하는 데에 있어서는 아래와 같은 사항이 중요하다. 19 | 20 | * 이름만 봐도 이 함수가 어떤 일을 하는 지 쉽게 알 수 있게 한다. 21 | * 함수를 작성한 사람과 사용하는 사람이 다르다면, 함수를 사용하는 사람의 입장에서 함수의 내부구현은 알 필요없이 사용할 수 있도록 인터페이스를 설계한다. 22 | 23 | 위와 같은 사항을 잘 지켜서 함수를 작성하면, ***어떤 일을 수행하는 긴 코드를 기능 별로 나누어 함수로 정의하고, 함수 호출을 사용해 코드를 작성하면 다른 프로그래머도 쉽게 프로그램을 이해하고 유지 보수가 가능하다.*** 24 | 25 | **절차지향 프로그래밍(procedural programming)** 을 간단히 정의하자면 함수를 설계할 때 위와 같은 사항들을 잘 지켜서 ***"이 프로그램은 어떤 일을 하는 가?"*** 에 대한 질문에 쉽게 답할 수 있도록 함수를 사용해 프로그래밍을 하는 것이라 할 수 있다. 26 | 27 | ## 3. 절차지향으로 학급 성적 평가 프로그램 만들기 28 | 예를 들어, 절차지향으로 엑셀에 저장된 학생들의 점수를 가져와 평균과 표준편차를 구하고, 이를 학년 전체 평균과 비교하는 프로그램을 Python을 이용해서 절차지향으로 작성한다고하면 `functions.py` script (module)에 엑셀에 저장된 학생들의 점수를 가져오는 함수, 평균을 구하는 함수, 분산을 구하는 함수 등을 정의해두고, 유저 프로그램인 `main.py` script에서 `functions.py` script (module)에 정의된 함수들을 불러와 사용할 수 있다. 이 예제에서 사용하는 데이터와 작성된 `functions.py`, `main.py` script는 아래와 같다. 29 | 30 | * `class_1.xlsx` 31 | 실제로 각 column의 이름에 대한 row는 존재하지 않음. 32 | 33 | | name | score | 34 | |---------|-------| 35 | | greg | 95 | 36 | | john | 25 | 37 | | yang | 50 | 38 | | timothy | 15 | 39 | | melisa | 100 | 40 | | thor | 10 | 41 | | elen | 25 | 42 | | mark | 80 | 43 | | steve | 95 | 44 | | anna | 20 | 45 | 46 | * `functions.py` 47 | 48 | ```python 49 | from openpyxl import load_workbook 50 | from functools import reduce 51 | import math 52 | 53 | def get_data_from_excel(filepath): 54 | 55 | wb = load_workbook(filename = filepath) 56 | ws = wb.active 57 | rows = ws.rows 58 | raw_data = {name_cell.value : score_cell.value for name_cell, score_cell in rows} 59 | scores = raw_data.values() 60 | return scores 61 | 62 | def get_average(scores): 63 | 64 | avrg = reduce(lambda score1, score2 : score1 + score2, scores) / len(scores) 65 | return avrg 66 | 67 | def get_variance(scores, avrg): 68 | 69 | tmp = 0 70 | for score in scores: 71 | tmp += (score - avrg)**2 72 | else: 73 | var = tmp / len(scores) 74 | return var 75 | 76 | def get_std_dev(var): 77 | 78 | std_dev = round(math.sqrt(var),1) 79 | return std_dev 80 | 81 | def evaluate_class(avrg, var, std_dev, total_avrg, total_std_dev): 82 | ''' 83 | evaluate_class(avrg, var, std_dev, total_avrg, total_std_dev) -> None 84 | 85 | Args: 86 | avrg : 반평균 87 | var : 반분산 88 | std_dev : 반표준편차 89 | total_avrg : 학년평균 90 | total_std_dev : 학년분산 91 | ''' 92 | print("평균:{}, 분산:{}, 표준편차:{}".format(avrg, var, std_dev)) 93 | if avrg < total_avrg and std_dev > total_std_dev: 94 | print('성적이 너무 저조하고 학생들의 실력 차이가 너무 크다.') 95 | elif avrg > total_avrg and std_dev > total_std_dev: 96 | print('성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!') 97 | elif avrg < total_avrg and std_dev < total_std_dev: 98 | print('학생들의 실력 차이는 크지 않지만 성적이 너무 저조하다. 주의 요망!') 99 | elif avrg > total_avrg and std_dev < total_std_dev: 100 | print('성적도 평균 이상이고 학생들의 실력 차이도 크지 않다.') 101 | ``` 102 | 103 | * `main.py` 104 | 105 | ```python 106 | from functions import * 107 | import argparse 108 | parser = argparse.ArgumentParser(prog = '평가프로그램', 109 | description = '엑셀에 저장된 학생들의 점수를 가져와 평균과 표준편차를 구하고, 학년 전체 평균과 비교하는 프로그램') 110 | parser.add_argument('filepath', type = str, help = '엑셀파일 저장경로') 111 | parser.add_argument('total_avrg', type = float, help = '학년평균') 112 | parser.add_argument('total_std_dev', type = float, help = '학년표준편차') 113 | args = parser.parse_args() 114 | 115 | def main(): 116 | scores = get_data_from_excel(filepath = args.filepath) 117 | avrg = get_average(scores = scores) 118 | var = get_variance(scores = scores, avrg = avrg) 119 | std_dev = get_std_dev(var = var) 120 | evaluate_class(avrg, var, std_dev, args.total_avrg, args.total_std_dev) 121 | 122 | if __name__ == '__main__': 123 | main() 124 | ``` 125 | 126 | 위에서 보듯이 절차지향으로 프로그래밍을 작성하면, 유저 프로그램인 `main.py` script에서는 코드가 매우 단순화 된 것을 볼 수 있다. 사용자는 아래처럼 사용하고자하는 프로그램을 실행시키기만하면된다. 127 | 128 | ```bash 129 | $ python main.py --help 130 | ``` 131 | 132 | ```bash 133 | usage: 평가프로그램 [-h] filepath total_avrg total_std_dev 134 | 135 | 엑셀에 저장된 학생들의 점수를 가져와 평균과 표준편차를 구하고, 학년 전체 평균과 비교하는 프로그램 136 | 137 | positional arguments: 138 | filepath 엑셀파일 저장경로 139 | total_avrg 학년평균 140 | total_std_dev 학년표준편차 141 | 142 | optional arguments: 143 | -h, --help show this help message and exit 144 | ``` 145 | 146 | ```bash 147 | $ python main.py ./class_1.xlsx 50 25 148 | ``` 149 | 150 | ```python 151 | 평균:51.5, 분산:1240.25, 표준편차:35.2 152 | 성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망! 153 | ``` 154 | 155 | ## 4. 객체지향 프로그래밍 156 | **객체지향 프로그래밍(object-oriented programming)** 은 ***"현실 세계에 존재하는 객체(object)를 어떻게 모델링(modeling) 할 것인 가?"*** 에 대한 물음에 대한 답이라고 볼 수 있다. 157 | 158 | ### 4.1 캡슐화 159 | 현실 세계의 **객체(object)** 를 구현하기 위해서는 **변수(variable)** 과 **함수(function)** 만 있으면 구현이 가능하다. 왜냐하면 ***객체가 지니는 특성은 변수로 나타낼 수 있고 객체의 행동 혹은 기능은 함수로 표현할 수 있기 때문이다.*** 이처럼 변수와 함수를 가진 객체를 이용하는 패러다임을 **객체지향 프로그래밍(object-oriented programming)** 이라고하며, 변수와 함수를 하나의 단위로 묶어서 구현하는 것과 더불어 구현 내용 일부를 **information hiding** 기법을 이용하여 감추는 데, 이 두 가지를 통칭하여 **캡슐화(encapsulation)** 라고 한다. 대부분의 프로그래밍 언어에서는 이를 **클래스(class)** 를 이용해 구현한다. 이는 Python에서도 마찬가지이다. 160 | 161 | ### 4.2 클래스를 사용해 객체 만들기 162 | 컴퓨터에서는 객체가 현실 세계의 사물을 모델링한 것이라는 중요한 의미를 모르며, 컴퓨터에게 객체란 그저 메모리의 한 단위일 뿐이다. 객체라는 메모리 공간을 할당한 다음 객체 안에 묶인 변수를 초기화하고 함수를 호출하는데 필요한 것이 **클래스(class)** 일 뿐이다. **클래스(class)** 는 ***객체를 생성해 내는 템플릿이고 객체는 클래스를 이용해 만들어진 변수와 함수를 가진 메모리 공간이며, 둘은 서로 다른 존재이며 메모리 공간도 다르다. 간단하게 정리하자면 아래와 같다.*** 163 | 164 | * **클래스(class)** : 현실 세계에 존재하는 객체(object)를 구현하는 설계도 165 | + in Python 166 | - 클래스 변수(class variable), 클래스 멤버(class member)라고도 부르는 변수를 정의할 수 있음 167 | - 인스턴스 모두가 공유하는 동일한 값 168 | - 인스턴스를 생성하지않고도 클래스만 선언한 상태에서 사용가능 169 | - oop에서 전역변수(global variable)을 대체하기위하여 사용 170 | - 클래스 메소드(class method)를 정의할 수 있음 171 | - oop에서 전역함수(global function)을 대체하기위해 사용할 때도 있으나, 주로 대체 생성자(constructor)를 만들 때 사용 172 | - Python에서 oop를 구현할 때, 전역함수(global function)을 대체하기위해서 클래스 메소드보다는 정적 메소드(static method)를 활용하는 것이 더 일반적 173 | * **인스턴스(instance)** : 클래스(class)라는 설계도로 현실 세계에 존재하는 객체(object)를 메모리공간에 생성해 낸 것 174 | + "이 객체는 특정 클래스의 인스턴스"라는 표현이 가능 175 | + 메모리 관점에서보면 객체와 인스턴스는 동일 176 | + in Python 177 | - 인스턴스 변수(instance variable), 인스턴스 멤버(instance member)라고도 부르는 변수를 정의할 수 있음 178 | - 인스턴스마다 값이 다른 변수, 인스턴스가 가지는 고유한 값 (상태) 179 | + 인스턴스 메소드(instance method)를 정의할 수 있음 180 | - 메소드 오버라이닝(method overriding)으로 다형성(polymorphism)을 구현할 수 있음 181 | 182 | 예를 들어, Python으로 간단한 `Person` 클래스를 구현하면 아래와 같다. 183 | 184 | ```python 185 | class Person: 186 | def __init__(self, name, money): 187 | self.name = name 188 | self.money = money 189 | 190 | def give_money(self, other, money): 191 | other.get_money(money) 192 | self.money -= money 193 | 194 | def get_money(self, money): 195 | self.money += money 196 | 197 | def __str__(self): 198 | return 'name : {}, money : {}'.format(self.name, self.money) 199 | ``` 200 | 201 | ```python 202 | greg = Person('greg', 5000) 203 | john = Person('john', 2000) 204 | 205 | print(greg, john) 206 | ``` 207 | 208 | ```bash 209 | name : greg, money : 5000 name : john, money : 2000 210 | ``` 211 | 212 | ```python 213 | greg.give_money(john, 2000) 214 | print(greg, john) 215 | ``` 216 | 217 | ```bash 218 | name : greg, money : 3000 name : john, money : 4000 219 | ``` 220 | ### 4.3 파이썬의 클래스 221 | 4.2 절에서 구현한 `Person` class를 아래의 코드를 이용해 분석해보면 `Person.give_money`, `Person.get_money` 모두 각각의 함수를 가리키고 있음을 알 수가 있다. 222 | 223 | ```python 224 | from pprint import pprint 225 | pprint(Person.__dict__) 226 | print(type(Person.give_money)) 227 | print(type(Person.get_money)) 228 | ``` 229 | 230 | ```bash 231 | mappingproxy({'__dict__': , 232 | '__doc__': None, 233 | '__init__': , 234 | '__module__': '__main__', 235 | '__str__': , 236 | '__weakref__': , 237 | 'get_money': , 238 | 'give_money': }) 239 | 240 | 241 | ``` 242 | 243 | 반면에 `Person` 클래스의 인스턴스인 `greg` 의 `greg.give_money`, `greg.get_money` 는 모두 `method` 이다. 이며 `Person.__dict__` 의 실행결과와는 다르게 `greg.__dict__` 는 인스턴스 변수의 목록을 `dict` 형태로 가지고 있는 **값 객체(value object)** 를 가리킨다. 244 | 245 | ```python 246 | print(type(greg.give_money)) 247 | print(type(greg.get_money)) 248 | print(greg.__dict__) 249 | ``` 250 | 251 | ```bash 252 | 253 | 254 | {'name': 'greg', 'money': 3000} 255 | ``` 256 | 257 | 아래 코드의 실행 결과를 보면 결국 `greg.give_money` 는 `Person.give_money` 를 가리키고 있는 함수를 가리키고, `greg.get_money` 역시 `Person.get_money` 가 가리키고 있는 함수를 가리키고 있음을 알 수 있다. 258 | 259 | ```python 260 | print(hex(id(greg.give_money.__func__))) 261 | print(hex(id(Person.give_money))) 262 | print(greg.give_money.__func__ is Person.give_money) 263 | ``` 264 | 265 | ```bash 266 | 0x10607cea0 267 | 0x10607cea0 268 | True 269 | ``` 270 | 271 | 아래의 코드의 실행 결과를 보면 `Person` 클래스에서 각각의 인스턴스 메소드(instance method)를 정의할 때, `self` 인자(argument)가 존재했는 데, `Person` 의 인스턴스인 `greg` 에서 `greg.give_money`, `greg.get_money` 등의 `method` 를 실행할 때는 `self` 인자에 매개변수(parameter)를 명시적으로 전달하지 않는 이유를 알 수 있는데, 그 이유는 ***인스턴스 메소드 내부에 함수와 인스턴스의 자신의 참조를 가지고 있으므로 함수에 직접 인스턴스의 참조를 전달할 수 있기 때문이다.*** 272 | 273 | ```python 274 | print(hex(id(greg.give_money.__self__))) 275 | print(hex(id(greg))) 276 | print(greg.give_money.__self__ is greg) 277 | ``` 278 | 279 | ```bash 280 | 0x1060a93c8 281 | 0x1060a93c8 282 | True 283 | ``` 284 | 285 | 위의 결과를 생각해보면 아래와 같은 코드로도 실행할 수 있음을 알 수 있다. 286 | 287 | ```python 288 | print(greg.__str__()) 289 | print(Person.__str__(greg)) 290 | ``` 291 | 292 | ```bash 293 | name : greg, money : 3000 294 | name : greg, money : 3000 295 | ``` 296 | 297 | 위에서 구현한 `Person` 클래스에는 클래스 변수(class variable)과 클래스 메소드(class method)가 없기 때문에 정보은닉을 위한 테크닉 중 하나인 **name mangling** 을 이용하여, `Person` 클래스의 클래스 변수 `__whole_population` 과 클래스 메소드 `__birth` 를 구현하고, **name mangling** 테크닉 없이 `check_population` 클래스 메소드를 구현한다. **name mangling** 테크닉은 클래스 변수이건 인스턴스 변수이건 간에 클래스 정의시 변수명을 `__변수명` 으로 정의하면, 클래스 외부에서 호출하려면 `_클래스명__변수명` 으로 호출할 수 있게 만드는 것을 의미한다. 클래스 변수와 클래스 메소드는 클래스를 요약할 때 상기한 특징 뿐만아니라 ***"인스턴스에서도 접근하거나 호출할 수 있다."*** 는 특징이 있다. (스태틱 메소드(static method)도 해당) 298 | 299 | ```python 300 | class Person: 301 | 302 | # 여기에 class variable (또는 class member) 303 | # instance 모두가 공유하는 동일한 값 304 | # instance를 생성하지않고도, class만 선언한 상태에서 호출이 가능하다. 305 | # oop에서 global variable을 대체하기위하여 사용 306 | __whole_population = 0 307 | # name mangling technique 사용, 외부에서 308 | ## Person.__whole_population으로 접근 불가 309 | ## Person._Person__whole_population으로 접근 가능 310 | 311 | # class method 312 | # oop에서 global에 선언된 function을 대체하기위해 사용 313 | # 대체 생성자를 만들 때, 더 많이씀 (여긴 대체생성자 구현하지 않음) 314 | @classmethod 315 | def __birth(cls): 316 | cls.__whole_population += 1 317 | 318 | @staticmethod 319 | def check_population(): 320 | return Person.__whole_population 321 | 322 | # instance method 323 | def __init__(self, name, money): # 생성자(constructor) 324 | Person.__birth() 325 | 326 | # instance variable (또는 instance member) 327 | # instance마다 값이 다른 변수, instance가 가지는 고유한 값 328 | # 여기에서는 self.name, self.age 329 | self.name = name 330 | self.money = money 331 | 332 | def get_money(self, money): 333 | self.money += money 334 | 335 | def give_money(self, other, money): 336 | # message passing 337 | # 다른 인스턴스(객체)랑 상호작용을 할 때, 상대 인스턴스(객체)의 인스턴스 변수를 바꿔야한다면 338 | other.get_money(money) # 이렇게하세요 339 | # other.money += money 이렇게하지마세요 340 | self.money -= money 341 | 342 | def __str__(self): 343 | return '{} : {}'.format(self.name, self.money) 344 | ``` 345 | 346 | ```python 347 | # 클래스 메소드는 인스턴스를 생성하지않고도 호출할 수 있다. 348 | print(Person.check_population()) 349 | ``` 350 | 351 | ```bash 352 | 0 353 | ``` 354 | 355 | ```python 356 | # 클래스 변수는 인스턴스간에 모두 공유한다. 357 | # 인스턴스를 통해서도 클래스 변수나 클래스 메소드를 호출할 수 있다. 358 | mark = Person('mark', 5000) 359 | greg = Person('greg', 3000) 360 | steve = Person('steve', 2000) 361 | 362 | print(Person.check_population()) 363 | print(Person._Person__whole_population) 364 | print(mark._Person__whole_population) 365 | print(greg._Person__whole_population) 366 | print(steve._Person__whole_population) 367 | 368 | steve._Person__birth() 369 | 370 | print(mark._Person__whole_population) 371 | print(greg._Person__whole_population) 372 | print(steve._Person__whole_population) 373 | ``` 374 | 375 | ```bash 376 | 3 377 | 3 378 | 3 379 | 3 380 | 3 381 | 4 382 | 4 383 | 4 384 | ``` 385 | 386 | ### 4.4 객체지향으로 은행 입출금 프로그램 만들기 387 | 위에 정리한 내용을 바탕으로 계좌 클래스를 구현하면 아래와 같다. 388 | 389 | ```python 390 | class Account: 391 | __num_acnt = 0 392 | 393 | @staticmethod 394 | def get_num_acnt(): 395 | return Account.__num_acnt 396 | 397 | def __init__(self, name, money): 398 | self._user = name 399 | self._balance = money 400 | 401 | Account.__num_acnt += 1 402 | 403 | def deposit(self, money): 404 | assert money > 0, '금액이 음수입니다.' 405 | self._balance += money 406 | 407 | def withdraw(self, money): 408 | assert money > 0, '금액이 음수입니다.' 409 | 410 | if self._balance >= money: 411 | self._balance -= money 412 | else: 413 | pass 414 | 415 | def transfer(self, other, money): 416 | assert money > 0, '금액이 음수입니다.' 417 | self.withdraw(money) 418 | if self._balance >= 0: 419 | other.deposit(money) 420 | return True 421 | else: 422 | return False 423 | 424 | def __str__(self): 425 | return 'user : {}, balance :{}'.format(self._user, self._balance) 426 | ``` 427 | 428 | ```python 429 | print(Account.get_num_acnt()) 430 | aisolab = Account(name = 'aisolab', money = 10000) 431 | modulab = Account(name = 'modulab', money = 5000) 432 | print(Account.get_num_acnt()) 433 | ``` 434 | 435 | ```bash 436 | 0 437 | 2 438 | ``` 439 | 440 | ```python 441 | aisolab.deposit(2000) 442 | print(aisolab) 443 | aisolab.withdraw(1000) 444 | print(aisolab) 445 | ``` 446 | 447 | ```bash 448 | user : aisolab, balance :12000 449 | user : aisolab, balance :11000 450 | ``` 451 | 452 | ```python 453 | aisolab.transfer(modulab, 1000) 454 | print(aisolab) 455 | print(modulab) 456 | ``` 457 | 458 | ```bash 459 | user : aisolab, balance :10000 460 | user : modulab, balance :6000 461 | ``` 462 | 463 | ### 4.5 정보 은닉 464 | **캡슐화(encapsulation)** 를 할 때, 어떤 클래스 변수(class variable)나 인스턴스 변수(instance variable)는 유저 프로그래머에게 공개하여 접근할 수 있도록 해야하고, 특정 클래스 변수나 인스턴스는 유저 프로그래머가 접근할 수 없도록 숨겨야하는데 이러한 개념을 **정보 은닉(information hiding)** 이라고 한다. Python에서는 기본적으로 **정보 은닉(information hiding)** 을 지원하지는 않으므로, Python에서 해당 개념을 구현 시 완벽하지는 않지만 비슷하게 **name mangilng** technique이나 `@property` decorator를 이용하여 구현한다. 먼저 C++에서 정보은닉이 구현된 코드를 Python으로 비슷하게 구현해보는 예제로 확인해보자. 465 | 466 | #### C++ 467 | 아래의 C++코드에서 봐야할 것은 `public` 과 `private` 키워드로 `public` 선언한 인스턴스 메소드(instance method)와 인스턴스 변수(instance variable)은 유저 프로그래머가 접근하거나 호출할 수 있으며, `private` 키워드로 선언한 인스턴스 메소드나 인스턴스 변수는 유저 프로그래머가 접근하거나 호출할 수 없다. 이러한 `public`, `private` 키워드를 **접근 제어 지시자(access modifier)** 라고하며 Python에는 이러한 키워드가 구현되어있지 않다. 468 | 469 | 아래의 코드에서 재미있는 점은 인스턴스 변수인 `balance` 는 `private` 키워드로 선언했기때문에, `class` 외부에서 `instance.balance` 와 같은 방식으로는 접근할 수 없으며, 유저 프로그래머는 반드시 `public` 키워드로 선언된 인스턴스 메소드인 `set_balance` 를 이용하여, 값을 변경해야하므로 `set_balance` 인스턴스 메소드에 특정 조건을 걸어 유저프로그래머가 이상한 값을 넣을 수 없도록 **방어적 프로그래밍(defensive programming)** 을 할 수 있다. 470 | 471 | ***객체지향 프로그래밍에서 잘된 정보 은닉은 필요한 인스턴스 메소드, 클래스 메소드만 공개하고 나머지는 모두 숨기는 것이다.*** 인스턴스 변수 또는 클래스 변수에 접근해야 할때는 **액세스 함수(access function)** 을 사용하여 접근하거나 변경하도록 설계해야한다. 아래의 예제에서는 `get_balance`, `set_balance` 인스턴스 메소드가 이에 해당한다. 472 | 473 | ```cpp 474 | #include 475 | #include 476 | using namespace std; 477 | 478 | class Account{ 479 | public://#1 480 | //생성자 : 파이썬 클래스의 __init__()과 같다 481 | Account(string name, int money){ 482 | user = name; 483 | balance = money; 484 | } 485 | //인스턴스 메서드(멤버 함수) 486 | int get_balance() { 487 | return balance; 488 | } 489 | //인스턴스 메서드(멤버 함수) 490 | void set_balance(int money) { 491 | if (money < 0) { 492 | return; 493 | } 494 | 495 | balance = money; 496 | } 497 | 498 | private://#2 499 | //인스턴스 멤버(멤버 변수) 500 | string user; 501 | int balance;//#3 502 | }; 503 | 504 | int main(void){ 505 | Account my_acnt("greg", 5000); 506 | 507 | //my_acnt.balance;//#4 508 | 509 | my_acnt.set_balance(-3000); // #5 510 | 511 | cout << my_acnt.get_balance() << endl; 512 | 513 | return 0; 514 | } 515 | ``` 516 | 517 | #### Python 518 | Python에서는 아래의 **두 가지 방법으로 C++에서는 지원하는 정보은닉을 비슷하게 구현할 수 있으며(완벽하게 정보은닉을 할 수 있다는 의미는 아님) 그 두 가지를 동시에 이용할 수도 있다.** 519 | * **name mangling** : 숨기려는 인스턴스 변수, 인스턴스 메소드, 클래스 변수, 클래스 메소드 앞에 `__` 붙이는 방법으로 `__` 을 붙여 인스턴스 변수, 클래스 변수, 인스턴스 메소드, 클래스 메소드를 정의하면 실제로는 `__class명_메소드명 또는 변수명` 으로 클래스 외부에서만 접근할 수 있다. 클래스 내부에서는 여전히 `__` 만 붙인 것으로 접근이 가능하다. 520 | 521 | ```python 522 | class Account: 523 | def __init__(self, name, money): 524 | self.__name = name 525 | self.__balance = money 526 | 527 | def get_balance(self): 528 | return self.__balance 529 | 530 | def set_balance(self, new_bal): 531 | if new_bal < 0: 532 | return 533 | self.__balance = new_bal 534 | ``` 535 | 536 | ```python 537 | my_acnt = Account(name = 'aisolab', money = 5000) 538 | print(my_acnt.__dict__) 539 | ``` 540 | 541 | ```bash 542 | {'_Account__name': 'aisolab', '_Account__balance': 5000} 543 | ``` 544 | 545 | ```python 546 | # 방어적 프로그래밍 547 | my_acnt.__balance = -5000 548 | print(my_acnt.__dict__) 549 | print(my_acnt._Account__balance) 550 | 551 | my_acnt.set_balance(-7000) 552 | print(my_acnt.__dict__) 553 | ``` 554 | 555 | ```bash 556 | {'_Account__name': 'aisolab', '_Account__balance': 5000, '__balance': -5000} 557 | 5000 558 | {'_Account__name': 'aisolab', '_Account__balance': 5000, '__balance': -5000} 559 | ``` 560 | 561 | * **property 기법** : `@property` decorator를 활용 562 | `@property` decorator를 이용하면, 인스턴스 변수 또는 클래스 변수를 호출하는 것 같지만 사실은 인스턴스 메소드, 클래스 메소드를 호출한다. 아래의 코드에서 유저 프로래머가 `my_acnt.balance` 로 직접 인스턴스 변수에 접근하는 것 같지만 사실은 `@property` 가 적용된 `balance` 인스턴스 메소드가 실행된 결과를 보게된다. 또한 `my_acnt.balance = -20` 과 같이 `my_acnt.balance` 가 **할당 연산자(assignment operator)** 인 `=` 를 만나서 `@balance.setter` 가 적용된 `balance` 인스턴스 메소드가 실행된다. 563 | 564 | ```python 565 | class Account: 566 | def __init__(self, name, money): 567 | self.__name = name 568 | self.balance = money 569 | 570 | @property 571 | def balance(self): # getter function 572 | return self.get_balance() 573 | 574 | @balance.setter 575 | def balance(self, new_bal): # setter function 576 | self.set_balance(new_bal) 577 | 578 | def get_balance(self): # original getter function 579 | return self.__balance 580 | 581 | def set_balance(self, new_bal): # original setter function 582 | if new_bal < 0: 583 | pass 584 | else: 585 | self.__balance = new_bal 586 | ``` 587 | 588 | ```python 589 | my_acnt = Account(name = 'aisolab', money = 5000) 590 | print(my_acnt.__dict__) 591 | ``` 592 | 593 | ```bash 594 | {'_Account__name': 'aisolab', '_Account__balance': 5000} 595 | ``` 596 | 597 | ```python 598 | # 인스턴스 변수에 접근한 것 같지만 사실은 @property가 적용된 599 | # balance()가 실행되어 return 값을 본 것 600 | print(my_acnt.balance) 601 | ``` 602 | 603 | ```bash 604 | 5000 605 | ``` 606 | 607 | ```python 608 | # 인스턴스 변수를 직접 수정한 것 같지만, my_acnt.balance가 = 를 만나서 609 | # @blance.setter가 적용된 blance()가 실행된 것 610 | my_acnt.balance = -20 611 | print(my_acnt.get_balance()) 612 | 613 | my_acnt.balance = 50 614 | print(my_acnt.get_balance()) 615 | ``` 616 | 617 | ```bash 618 | 5000 619 | 50 620 | ``` 621 | ## 5. 객체지향으로 다시 만드는 학급 성적 평가 프로그램 622 | **3. 절차지향으로 학습 성적 평가 프로그램 만들기** 절에서 작성한 객체지향 프로그래밍을 이용해서, 사용자 프로그램인 `main.py` script를 더 단순하게 작성할 수 있다. 623 | 624 | * `statistics.py` 625 | 626 | ```python 627 | from functools import reduce 628 | import math 629 | 630 | class Stat: 631 | 632 | def get_average(self, scores): 633 | 634 | avrg = reduce(lambda score1, score2 : score1 + score2, scores) / len(scores) 635 | return avrg 636 | 637 | def get_variance(self, scores, avrg): 638 | 639 | tmp = 0 640 | for score in scores: 641 | tmp += (score - avrg)**2 642 | else: 643 | var = tmp / len(scores) 644 | return var 645 | 646 | def get_std_dev(self, var): 647 | 648 | std_dev = round(math.sqrt(var),1) 649 | return std_dev 650 | ``` 651 | 652 | * `datahandler.py` 653 | 654 | ```python 655 | from openpyxl import load_workbook 656 | from statistics import Stat 657 | 658 | class DataHandler: 659 | evaluator = Stat() 660 | 661 | @classmethod 662 | def get_data_from_excel(cls, filepath): 663 | 664 | wb = load_workbook(filename = filepath) 665 | ws = wb.active 666 | rows = ws.rows 667 | raw_data = {name_cell.value : score_cell.value for name_cell, score_cell in rows} 668 | scores = raw_data.values() 669 | return scores 670 | 671 | def __init__(self, filepath): 672 | self.scores = DataHandler.get_data_from_excel(filepath = filepath) 673 | self.cache = {'scores' : self.scores} 674 | 675 | def get_average(self): 676 | 677 | if 'average' not in self.cache.keys(): 678 | self.cache.update({'average' : DataHandler.evaluator.get_average(self.cache.get('scores'))}) 679 | return self.cache.get('average') 680 | else: 681 | return self.cache.get('average') 682 | 683 | def get_variance(self): 684 | 685 | if 'variance' not in self.cache.keys(): 686 | self.cache.update({'variance' : DataHandler.evaluator.get_variance(self.cache.get('scores'), self.get_average())}) 687 | return self.cache.get('variance') 688 | else: 689 | return self.cache.get('variance') 690 | 691 | def get_std_dev(self): 692 | 693 | if 'std_dev' not in self.cache.keys(): 694 | self.cache.update({'std_dev' : DataHandler.evaluator.get_std_dev(self.get_variance())}) 695 | return self.cache.get('std_dev') 696 | else: 697 | return self.cache.get('std_dev') 698 | 699 | def evaluate_class(self, total_avrg, total_std_dev): 700 | avrg = self.get_average() 701 | var = self.get_variance() 702 | std_dev = self.get_std_dev() 703 | 704 | print("평균:{}, 분산:{}, 표준편차:{}".format(avrg, var, std_dev)) 705 | if avrg < total_avrg and std_dev > total_std_dev: 706 | print('성적이 너무 저조하고 학생들의 실력 차이가 너무 크다.') 707 | elif avrg > total_avrg and std_dev > total_std_dev: 708 | print('성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!') 709 | elif avrg < total_avrg and std_dev < total_std_dev: 710 | print('학생들의 실력 차이는 크지 않지만 성적이 너무 저조하다. 주의 요망!') 711 | elif avrg > total_avrg and std_dev < total_std_dev: 712 | print('성적도 평균 이상이고 학생들의 실력 차이도 크지 않다.') 713 | ``` 714 | 715 | * `main.py` 716 | 717 | ```python 718 | from datahandler import DataHandler 719 | import argparse 720 | parser = argparse.ArgumentParser(prog = '평가프로그램', 721 | description = '엑셀에 저장된 학생들의 점수를 가져와 평균과 표준편차를 구하고, 학년 전체 평균과 비교하는 프로그램') 722 | parser.add_argument('filepath', type = str, help = '엑셀파일 저장경로') 723 | parser.add_argument('total_avrg', type = float, help = '학년평균') 724 | parser.add_argument('total_std_dev', type = float, help = '학년표준편차') 725 | args = parser.parse_args() 726 | 727 | def main(): 728 | datahandler = DataHandler(filepath = args.filepath) 729 | datahandler.evaluate_class(total_avrg = args.total_avrg, total_std_dev = args.total_std_dev) 730 | 731 | if __name__ == '__main__': 732 | main() 733 | ``` 734 | 735 | 유저 프로그램인 `main.py` script를 실행시키면 아래와 같다. 736 | 737 | ```bash 738 | $ python main.py --help 739 | ``` 740 | 741 | ```bash 742 | usage: 평가프로그램 [-h] filepath total_avrg total_std_dev 743 | 744 | 엑셀에 저장된 학생들의 점수를 가져와 평균과 표준편차를 구하고, 학년 전체 평균과 비교하는 프로그램 745 | 746 | positional arguments: 747 | filepath 엑셀파일 저장경로 748 | total_avrg 학년평균 749 | total_std_dev 학년표준편차 750 | 751 | optional arguments: 752 | -h, --help show this help message and exit 753 | ``` 754 | 755 | ```bash 756 | $ python main.py ./class_1.xlsx 50 25 757 | ``` 758 | 759 | ```bash 760 | 평균:51.5, 분산:1240.25, 표준편차:35.2 761 | 성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망! 762 | ``` --------------------------------------------------------------------------------